oas_rails 1.3.6 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/oas_rails/oas_rails_controller.rb +11 -2
- data/app/helpers/oas_rails/oas_rails_helper.rb +4 -4
- data/lib/oas_rails/active_record_example_finder.rb +4 -2
- data/lib/oas_rails/builders/esquema_builder.rb +8 -4
- data/lib/oas_rails/builders/oas_route_builder.rb +5 -4
- data/lib/oas_rails/configuration.rb +2 -1
- data/lib/oas_rails/extractors/render_response_extractor.rb +6 -0
- data/lib/oas_rails/extractors/route_extractor.rb +35 -24
- data/lib/oas_rails/version.rb +1 -1
- data/lib/oas_rails.rb +20 -11
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25a9bae1a6533ab3bf3b0ea8c00741c4bd07adc76e06aa5526d1cd9fa8c58d34
|
|
4
|
+
data.tar.gz: e75c2b1f815498fda47957d426aa6a39ce71b64cac89b1cf91d58ccea882ed3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8f82f7344bd96c8e4e70d65066cef4e1c647b7191318d681057df96e20e3ba7bc88346a85766f82c3e383d1548bba3e3bb9ff5c2a8ed980538ee800531765a2
|
|
7
|
+
data.tar.gz: ccf3a4c6566a3457b877aee8ba59bb3f8d9bfcf8bd4ee4a157e2c3033800704d6bf7b840b97155700535882d3077f99d0cd277f0fa37d986f96dff7fc38bc0fd
|
|
@@ -3,13 +3,22 @@ module OasRails
|
|
|
3
3
|
# Include URL help if the layout is a user-customized layout.
|
|
4
4
|
include Rails.application.routes.url_helpers
|
|
5
5
|
|
|
6
|
+
before_action :resolve_config
|
|
7
|
+
|
|
6
8
|
def index
|
|
7
9
|
respond_to do |format|
|
|
8
|
-
format.html { render "index", layout:
|
|
10
|
+
format.html { render "index", layout: @config.layout }
|
|
9
11
|
format.json do
|
|
10
|
-
render json: OasRails.build.to_json, status: :ok
|
|
12
|
+
render json: OasRails.build(config: @config).to_json, status: :ok
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def resolve_config
|
|
20
|
+
config_name = params.dig("default", "configuration")&.to_sym || :default
|
|
21
|
+
@config = OasRails.config(config_name)
|
|
22
|
+
end
|
|
14
23
|
end
|
|
15
24
|
end
|
|
@@ -2,7 +2,7 @@ module OasRails
|
|
|
2
2
|
module OasRailsHelper # rubocop:disable Metrics/ModuleLength
|
|
3
3
|
def rapidoc_configuration_defaults
|
|
4
4
|
{
|
|
5
|
-
"spec-url" => "#{
|
|
5
|
+
"spec-url" => "#{request.script_name}.json",
|
|
6
6
|
"show-header" => "false",
|
|
7
7
|
"font-size" => "largest",
|
|
8
8
|
"show-method-in-nav-bar" => "as-colored-text",
|
|
@@ -16,13 +16,13 @@ module OasRails
|
|
|
16
16
|
|
|
17
17
|
def rapidoc_configuration_attributes
|
|
18
18
|
rapidoc_configuration_defaults.merge(
|
|
19
|
-
rapidoc_theme(
|
|
20
|
-
|
|
19
|
+
rapidoc_theme(@config.rapidoc_theme),
|
|
20
|
+
@config.rapidoc_configuration
|
|
21
21
|
).map { |k, v| %(#{k}=#{ERB::Util.html_escape(v)}) }.join(' ')
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def rapidoc_logo_url
|
|
25
|
-
|
|
25
|
+
@config.rapidoc_logo_url
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
THEMES = {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
module OasRails
|
|
2
2
|
class ActiveRecordExampleFinder
|
|
3
|
-
def initialize(context: :incoming, utils: Utils, factory_bot: FactoryBot, erb: ERB, yaml: YAML, file: File)
|
|
3
|
+
def initialize(context: :incoming, utils: Utils, factory_bot: FactoryBot, erb: ERB, yaml: YAML, file: File, config: nil)
|
|
4
4
|
@context = context
|
|
5
5
|
@utils = utils
|
|
6
6
|
@factory_bot = factory_bot
|
|
7
7
|
@erb = erb
|
|
8
8
|
@yaml = yaml
|
|
9
9
|
@file = file
|
|
10
|
+
@config = config
|
|
10
11
|
@factory_examples = {}
|
|
11
12
|
end
|
|
12
13
|
|
|
@@ -59,7 +60,8 @@ module OasRails
|
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def clean_example_object(obj:)
|
|
62
|
-
|
|
63
|
+
resolved_config = @config || OasRails.config
|
|
64
|
+
obj.reject { |key, _| resolved_config.send("excluded_columns_#{@context}").include?(key.to_sym) }
|
|
63
65
|
end
|
|
64
66
|
end
|
|
65
67
|
end
|
|
@@ -5,12 +5,14 @@ module OasRails
|
|
|
5
5
|
# Builds a schema for a class when it is used as incoming API data.
|
|
6
6
|
#
|
|
7
7
|
# @param klass [Class] The class for which the schema is built.
|
|
8
|
+
# @param config [OasRails::Configuration, nil] Optional config override; falls back to OasRails.config.
|
|
8
9
|
# @return [Hash] The schema as a JSON-compatible hash.
|
|
9
|
-
def build_incoming_schema(klass:, model_to_schema_class: EasyTalk)
|
|
10
|
+
def build_incoming_schema(klass:, model_to_schema_class: EasyTalk, config: nil)
|
|
11
|
+
resolved_config = config || OasRails.config
|
|
10
12
|
build_schema(
|
|
11
13
|
klass: klass,
|
|
12
14
|
model_to_schema_class: model_to_schema_class,
|
|
13
|
-
excluded_columns:
|
|
15
|
+
excluded_columns: resolved_config.excluded_columns_incoming,
|
|
14
16
|
exclude_primary_key: true
|
|
15
17
|
)
|
|
16
18
|
end
|
|
@@ -18,12 +20,14 @@ module OasRails
|
|
|
18
20
|
# Builds a schema for a class when it is used as outgoing API data.
|
|
19
21
|
#
|
|
20
22
|
# @param klass [Class] The class for which the schema is built.
|
|
23
|
+
# @param config [OasRails::Configuration, nil] Optional config override; falls back to OasRails.config.
|
|
21
24
|
# @return [Hash] The schema as a JSON-compatible hash.
|
|
22
|
-
def build_outgoing_schema(klass:, model_to_schema_class: EasyTalk)
|
|
25
|
+
def build_outgoing_schema(klass:, model_to_schema_class: EasyTalk, config: nil)
|
|
26
|
+
resolved_config = config || OasRails.config
|
|
23
27
|
build_schema(
|
|
24
28
|
klass: klass,
|
|
25
29
|
model_to_schema_class: model_to_schema_class,
|
|
26
|
-
excluded_columns:
|
|
30
|
+
excluded_columns: resolved_config.excluded_columns_outgoing,
|
|
27
31
|
exclude_primary_key: false
|
|
28
32
|
)
|
|
29
33
|
end
|
|
@@ -7,12 +7,13 @@ module OasRails
|
|
|
7
7
|
# source file directly so that OasRails annotations remain readable.
|
|
8
8
|
KNOWN_RUNTIME_WRAPPERS = %w[sorbet-runtime].freeze
|
|
9
9
|
|
|
10
|
-
def self.build_from_rails_route(rails_route)
|
|
11
|
-
new(rails_route).build
|
|
10
|
+
def self.build_from_rails_route(rails_route, config:)
|
|
11
|
+
new(rails_route, config:).build
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def initialize(rails_route)
|
|
14
|
+
def initialize(rails_route, config:)
|
|
15
15
|
@rails_route = rails_route
|
|
16
|
+
@config = config
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def build
|
|
@@ -46,7 +47,7 @@ module OasRails
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def path
|
|
49
|
-
|
|
50
|
+
@config.prefix_path + @config.route_extractor.clean_route(@rails_route.path.spec.to_s)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def source_string
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
module OasRails
|
|
2
2
|
class Configuration < OasCore::Configuration
|
|
3
3
|
attr_accessor :autodiscover_request_body, :autodiscover_responses, :ignored_actions, :rapidoc_theme, :layout, :source_oas_path, :rapidoc_configuration, :rapidoc_logo_url
|
|
4
|
-
attr_reader :route_extractor, :include_mode, :mounted_path, :prefix_path
|
|
4
|
+
attr_reader :name, :route_extractor, :include_mode, :mounted_path, :prefix_path
|
|
5
5
|
|
|
6
6
|
def initialize
|
|
7
7
|
super
|
|
8
|
+
@name = :default
|
|
8
9
|
@mounted_path = ""
|
|
9
10
|
self.prefix_path = ENV["RAILS_RELATIVE_URL_ROOT"] || Rails.application.config.relative_url_root || ""
|
|
10
11
|
@route_extractor = Extractors::RouteExtractor
|
|
@@ -2,6 +2,12 @@ module OasRails
|
|
|
2
2
|
module Extractors
|
|
3
3
|
# Extracts and processes render responses from a given source.
|
|
4
4
|
module RenderResponseExtractor
|
|
5
|
+
# NOTE: This module is invoked as a callback by OasCore during the build pipeline.
|
|
6
|
+
# Because OasCore controls the call site, it is not possible to inject a named config
|
|
7
|
+
# here via dependency injection. As a result, calls to EsquemaBuilder and
|
|
8
|
+
# ActiveRecordExampleFinder from this module will use OasRails.config(:default).
|
|
9
|
+
# This is a known limitation of the multi-config feature.
|
|
10
|
+
|
|
5
11
|
class << self
|
|
6
12
|
# Extracts responses from the provided source string.
|
|
7
13
|
#
|
|
@@ -10,25 +10,36 @@ module OasRails
|
|
|
10
10
|
].freeze
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
|
-
def host_routes_by_path(path)
|
|
14
|
-
@
|
|
15
|
-
@
|
|
13
|
+
def host_routes_by_path(path, config:)
|
|
14
|
+
@host_routes_cache ||= {}
|
|
15
|
+
@host_routes_cache[config.name] ||= extract_host_routes(config:)
|
|
16
|
+
@host_routes_cache[config.name].select { |r| r.path == path }
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
def host_routes
|
|
19
|
-
@
|
|
19
|
+
def host_routes(config:)
|
|
20
|
+
@host_routes_cache ||= {}
|
|
21
|
+
@host_routes_cache[config.name] ||= extract_host_routes(config:)
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
# Clear
|
|
24
|
+
# Clear the cached routes.
|
|
23
25
|
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
# When a +config+ is given, only that config's cache entry is cleared,
|
|
27
|
+
# forcing the next call to re-extract routes for that configuration.
|
|
28
|
+
# When called without arguments, the entire cache is cleared for all
|
|
29
|
+
# configurations.
|
|
30
|
+
def clear_cache(config: nil)
|
|
31
|
+
if config
|
|
32
|
+
@host_routes_cache&.delete(config.name)
|
|
33
|
+
@host_paths_cache&.delete(config.name)
|
|
34
|
+
else
|
|
35
|
+
@host_routes_cache = nil
|
|
36
|
+
@host_paths_cache = nil
|
|
37
|
+
end
|
|
28
38
|
end
|
|
29
39
|
|
|
30
|
-
def host_paths
|
|
31
|
-
@
|
|
40
|
+
def host_paths(config:)
|
|
41
|
+
@host_paths_cache ||= {}
|
|
42
|
+
@host_paths_cache[config.name] ||= host_routes(config:).map(&:path).uniq.sort
|
|
32
43
|
end
|
|
33
44
|
|
|
34
45
|
def clean_route(route)
|
|
@@ -37,25 +48,25 @@ module OasRails
|
|
|
37
48
|
|
|
38
49
|
private
|
|
39
50
|
|
|
40
|
-
def extract_host_routes
|
|
41
|
-
routes = valid_routes.map { |r| Builders::OasRouteBuilder.build_from_rails_route(r) }
|
|
51
|
+
def extract_host_routes(config:)
|
|
52
|
+
routes = valid_routes(config:).map { |r| Builders::OasRouteBuilder.build_from_rails_route(r, config:) }
|
|
42
53
|
|
|
43
|
-
routes.select! { |route| route.tags.any? } if
|
|
44
|
-
routes.select! { |route| route.tags.any? { |t| t.tag_name == "oas_include" } } if
|
|
54
|
+
routes.select! { |route| route.tags.any? } if config.include_mode == :with_tags
|
|
55
|
+
routes.select! { |route| route.tags.any? { |t| t.tag_name == "oas_include" } } if config.include_mode == :explicit
|
|
45
56
|
routes
|
|
46
57
|
end
|
|
47
58
|
|
|
48
|
-
def valid_routes
|
|
59
|
+
def valid_routes(config:)
|
|
49
60
|
Rails.application.routes.routes.select do |route|
|
|
50
|
-
valid_api_route?(route)
|
|
61
|
+
valid_api_route?(route, config:)
|
|
51
62
|
end
|
|
52
63
|
end
|
|
53
64
|
|
|
54
|
-
def valid_api_route?(route)
|
|
65
|
+
def valid_api_route?(route, config:)
|
|
55
66
|
return false unless valid_route_implementation?(route)
|
|
56
67
|
return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) }
|
|
57
|
-
return false unless route.path.spec.to_s.start_with?(
|
|
58
|
-
return false if ignore_custom_actions?(route)
|
|
68
|
+
return false unless route.path.spec.to_s.start_with?(config.api_path)
|
|
69
|
+
return false if ignore_custom_actions?(route, config:)
|
|
59
70
|
|
|
60
71
|
true
|
|
61
72
|
end
|
|
@@ -88,9 +99,9 @@ module OasRails
|
|
|
88
99
|
# Support controller name only to ignore all controller actions.
|
|
89
100
|
# Support ignoring "controller#action"
|
|
90
101
|
# Ignoring "controller#action" AND "api_path/controller#action"
|
|
91
|
-
def ignore_custom_actions?(route)
|
|
92
|
-
api_path = "#{
|
|
93
|
-
ignored_actions =
|
|
102
|
+
def ignore_custom_actions?(route, config:)
|
|
103
|
+
api_path = "#{config.api_path.sub(%r{\A/}, '')}/".sub(%r{/+$}, '/')
|
|
104
|
+
ignored_actions = config.ignored_actions.flat_map do |custom_route|
|
|
94
105
|
if custom_route.start_with?(api_path)
|
|
95
106
|
[custom_route]
|
|
96
107
|
else
|
data/lib/oas_rails/version.rb
CHANGED
data/lib/oas_rails.rb
CHANGED
|
@@ -24,39 +24,48 @@ module OasRails
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
class << self
|
|
27
|
+
# NOTE: This lambda is called back by OasCore during build and has no way to receive
|
|
28
|
+
# a named config as a parameter. It falls back to OasRails.config(:default).
|
|
29
|
+
# This is a known limitation: excluded_columns in EsquemaBuilder will always use
|
|
30
|
+
# the default config when invoked from this re-entry point.
|
|
27
31
|
OasCore::JsonSchemaGenerator.register_type_parser(
|
|
28
32
|
->(t) { Utils.active_record_class?(t) },
|
|
29
33
|
->(type, _required) { Builders::EsquemaBuilder.build_outgoing_schema(klass: type.constantize) }
|
|
30
34
|
)
|
|
31
35
|
|
|
32
|
-
def build
|
|
33
|
-
clear_cache
|
|
36
|
+
def build(config: OasRails.config)
|
|
37
|
+
clear_cache(config:)
|
|
34
38
|
OasCore.config = config
|
|
35
39
|
|
|
36
|
-
host_routes = config.route_extractor.host_routes
|
|
37
|
-
oas_source = config.source_oas_path ? read_source_oas_file : {}
|
|
40
|
+
host_routes = config.route_extractor.host_routes(config:)
|
|
41
|
+
oas_source = config.source_oas_path ? read_source_oas_file(config:) : {}
|
|
38
42
|
|
|
39
43
|
OasCore.build(host_routes, oas_source: oas_source)
|
|
40
44
|
end
|
|
41
45
|
|
|
42
|
-
def configure
|
|
43
|
-
|
|
46
|
+
def configure(name = :default)
|
|
47
|
+
cfg = Configuration.new
|
|
48
|
+
yield cfg
|
|
49
|
+
cfg.instance_variable_set(:@name, name)
|
|
50
|
+
@configs ||= {}
|
|
51
|
+
@configs[name] = cfg
|
|
44
52
|
end
|
|
45
53
|
|
|
46
|
-
def config
|
|
47
|
-
@
|
|
54
|
+
def config(name = :default)
|
|
55
|
+
@configs ||= {}
|
|
56
|
+
@configs[name] ||= Configuration.new
|
|
48
57
|
end
|
|
49
58
|
|
|
50
59
|
private
|
|
51
60
|
|
|
52
|
-
def clear_cache
|
|
61
|
+
def clear_cache(config:)
|
|
53
62
|
return if Rails.env.production?
|
|
54
63
|
|
|
55
64
|
MethodSource.clear_cache
|
|
56
|
-
OasRails::Extractors::RouteExtractor.clear_cache
|
|
65
|
+
OasRails::Extractors::RouteExtractor.clear_cache(config:)
|
|
57
66
|
end
|
|
58
67
|
|
|
59
|
-
def read_source_oas_file
|
|
68
|
+
def read_source_oas_file(config:)
|
|
60
69
|
file_path = Rails.root.join(config.source_oas_path)
|
|
61
70
|
JSON.parse(File.read(file_path), symbolize_names: true)
|
|
62
71
|
rescue Errno::ENOENT => e
|