Class: Aspera::Schema::Registry
- Inherits:
-
Object
- Object
- Aspera::Schema::Registry
- Includes:
- Singleton
- Defined in:
- lib/aspera/schema/registry.rb
Constant Summary collapse
- LOCATIONS =
{ spec: 'aspera/transfer/spec.schema.yaml', args: 'aspera/sync/args.schema.yaml', conf: 'aspera/sync/conf.schema.yaml', opts: 'aspera/cli/options.schema.yaml', aoc: 'aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml', automation: 'aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml', faspex: 'aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml', faspio: 'aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml', console: 'aspera/schema/IBM Aspera Console-enhanced.yaml', node: 'aspera/schema/IBM Aspera Node API-4.4.6.yaml', shares: 'aspera/schema/IBM_Aspera_Shares.yaml', async_tables: 'aspera/schema/async_tables.yaml' }
- OWNED =
Schemas owned by ascli: values are validated against them (vendor API schemas are validated by the API)
i[spec args conf opts async_tables].freeze
- OPTIONS =
'opts'- TRANSFER_SPEC =
'spec'- SYNC_CONF =
'conf'- SYNC_ARGS =
'args'- AOC =
'aoc'- AUTOMATION =
'automation'- FASPEX =
'faspex'- FASPIO =
'faspio'- CONSOLE =
'console'- NODE =
'node'- SHARES =
'shares+/api/v1'- ASYNC_TABLES =
'async_tables'- LOG_OPTIONS =
"#{OPTIONS}:components.schemas.LogOptions"- DIRECT_AGENT_OPTIONS =
"#{OPTIONS}:components.schemas.DirectAgentOptions"- NODE_AGENT_OPTIONS =
"#{OPTIONS}:components.schemas.NodeAgentOptions"- HTTPGW_AGENT_OPTIONS =
"#{OPTIONS}:components.schemas.HttpgwAgentOptions"- TRANSFERD_AGENT_OPTIONS =
"#{OPTIONS}:components.schemas.TransferdAgentOptions"- TRANSFER_AGENT_OPTIONS =
"#{OPTIONS}:components.schemas.TransferAgentOptions"- SMTP_OPTIONS =
"#{OPTIONS}:components.schemas.SmtpOptions"- HTTP_OPTIONS =
"#{OPTIONS}:components.schemas.HttpOptions"- VAULT_OPTIONS =
"#{OPTIONS}:components.schemas.VaultOptions"- VAULT_SECRET =
"#{OPTIONS}:components.schemas.VaultSecret"- IMAGE_OPTIONS =
"#{OPTIONS}:components.schemas.ImageOptions"- OUT_OPTIONS =
"#{OPTIONS}:components.schemas.OutOptions"- PACKAGE_FOLDER_OPTIONS =
"#{OPTIONS}:components.schemas.PackageFolderOptions"- REQ_BODY =
'.requestBody.content.application/json.schema'- QUERY_PARAMS_SUFFIX =
Suffix appended to a dotted path to signal query-param extraction in reader()
'.parameters'
Class Method Summary collapse
-
.instance ⇒ Registry
Returns the singleton instance of Registry.
- .known?(sym) ⇒ Boolean
-
.owned?(name_path) ⇒ Boolean
trueif schema is owned by ascli (not a vendor API). -
.query_params(component, endpoint, method: 'get') ⇒ String
Get path to query parameters for a GET endpoint.
-
.req_body(component, endpoint) ⇒ String
Get path to request body schema, no check if it exists.
-
.split_component(component) ⇒ Array(String, String)
Split a component string into registry key and optional path prefix.
Instance Method Summary collapse
-
#initialize ⇒ Registry
constructor
A new instance of Registry.
-
#reader(name_path) ⇒ Reader
Read schema from file or from cache.
Constructor Details
#initialize ⇒ Registry
Returns a new instance of Registry.
104 105 106 107 |
# File 'lib/aspera/schema/registry.rb', line 104 def initialize @cache = {} @main_folder = File.('../..', __dir__) end |
Class Method Details
.instance ⇒ Registry
Returns the singleton instance of Registry
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/aspera/schema/registry.rb', line 13 class Registry include Singleton class << self def known?(sym) LOCATIONS.key?(sym) end # @param name_path [String] schema path, e.g. `opts:components.schemas.HttpOptions` # @return [Boolean] `true` if schema is owned by ascli (not a vendor API) def owned?(name_path) OWNED.include?(name_path.split(':', 2).first.to_sym) end # Split a component string into registry key and optional path prefix. # Syntax: 'key' or 'key+/prefix' (e.g. 'shares+/api/v1') # @param component [String] registry key with optional '+/prefix' # @return [Array(String, String)] [key, prefix] where prefix may be '' def split_component(component) key, prefix = component.split('+', 2) [key, prefix || ''] end # Get path to request body schema, no check if it exists # @param component [String] registry key, optionally with path prefix (e.g. 'shares+/api/v1') # @param endpoint [String] endpoint path without leading slash (e.g. 'data/shares.post') # @return [String] schema path usable in schema: keyword def req_body(component, endpoint) key, prefix = split_component(component) "#{key}:paths.#{prefix}/#{endpoint}.requestBody.content.application/json.schema" end # Get path to query parameters for a GET endpoint # @param component [String] registry key, optionally with path prefix (e.g. 'shares+/api/v1') # @param endpoint [String] resource path without leading slash (e.g. 'data/shares') # @param method [String] HTTP method (default: 'get') # @return [String] schema path usable in query_schema: keyword def query_params(component, endpoint, method: 'get') key, prefix = split_component(component) "#{key}:paths.#{prefix}/#{endpoint}.#{method}#{QUERY_PARAMS_SUFFIX}" end end LOCATIONS = { spec: 'aspera/transfer/spec.schema.yaml', args: 'aspera/sync/args.schema.yaml', conf: 'aspera/sync/conf.schema.yaml', opts: 'aspera/cli/options.schema.yaml', aoc: 'aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml', automation: 'aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml', faspex: 'aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml', faspio: 'aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml', console: 'aspera/schema/IBM Aspera Console-enhanced.yaml', node: 'aspera/schema/IBM Aspera Node API-4.4.6.yaml', shares: 'aspera/schema/IBM_Aspera_Shares.yaml', async_tables: 'aspera/schema/async_tables.yaml' } # Schemas owned by ascli: values are validated against them (vendor API schemas are validated by the API) OWNED = i[spec args conf opts async_tables].freeze OPTIONS = 'opts' TRANSFER_SPEC = 'spec' SYNC_CONF = 'conf' SYNC_ARGS = 'args' AOC = 'aoc' AUTOMATION = 'automation' FASPEX = 'faspex' FASPIO = 'faspio' CONSOLE = 'console' NODE = 'node' SHARES = 'shares+/api/v1' ASYNC_TABLES = 'async_tables' LOG_OPTIONS = "#{OPTIONS}:components.schemas.LogOptions" DIRECT_AGENT_OPTIONS = "#{OPTIONS}:components.schemas.DirectAgentOptions" NODE_AGENT_OPTIONS = "#{OPTIONS}:components.schemas.NodeAgentOptions" HTTPGW_AGENT_OPTIONS = "#{OPTIONS}:components.schemas.HttpgwAgentOptions" TRANSFERD_AGENT_OPTIONS = "#{OPTIONS}:components.schemas.TransferdAgentOptions" TRANSFER_AGENT_OPTIONS = "#{OPTIONS}:components.schemas.TransferAgentOptions" SMTP_OPTIONS = "#{OPTIONS}:components.schemas.SmtpOptions" HTTP_OPTIONS = "#{OPTIONS}:components.schemas.HttpOptions" VAULT_OPTIONS = "#{OPTIONS}:components.schemas.VaultOptions" VAULT_SECRET = "#{OPTIONS}:components.schemas.VaultSecret" IMAGE_OPTIONS = "#{OPTIONS}:components.schemas.ImageOptions" OUT_OPTIONS = "#{OPTIONS}:components.schemas.OutOptions" PACKAGE_FOLDER_OPTIONS = "#{OPTIONS}:components.schemas.PackageFolderOptions" REQ_BODY = '.requestBody.content.application/json.schema' # Suffix appended to a dotted path to signal query-param extraction in reader() QUERY_PARAMS_SUFFIX = '.parameters' def initialize @cache = {} @main_folder = File.('../..', __dir__) end # Read schema from file or from cache. # When name_path ends with QUERY_PARAMS_SUFFIX, the OAS `parameters` array at that path # is synthesised into an object schema via Reader.from_query_params instead of navigating # into the tree. # @param name_path [String] registry key with optional colon-separated dotted path suffix, # e.g. "faspex:paths./packages.get.parameters" or "faspex:paths./packages.post.requestBody..." # @return [Reader] schema reader def reader(name_path) name, path = name_path.split(':', 2) sym = name.to_sym Aspera.assert(Registry.known?(sym)) { "schema: #{sym}" } spec_file = File.join(@main_folder, LOCATIONS[sym]) @cache[sym] = Yaml.safe_load(File.read(spec_file)) if spec_file.end_with?('.yaml') && !@cache.key?(sym) @cache[sym] = JSON.parse(File.read(spec_file)) if spec_file.end_with?('.json') && !@cache.key?(sym) # Query-params path: strip the suffix, navigate to the operation node, extract parameters if path&.end_with?(QUERY_PARAMS_SUFFIX) parent_path = path.delete_suffix(QUERY_PARAMS_SUFFIX) node = @cache[sym].dig(*parent_path.split('.')) # Parameters may be references to components.parameters params = (node&.fetch('parameters', []) || []).map do |param| param.key?('$ref') ? @cache[sym].dig(*param['$ref'].delete_prefix('#/').split('/')) : param end return Reader.from_query_params(params.compact) end reader = Reader.new(@cache[sym]) return reader unless path reader.dig(*path.split('.')) end end |
.known?(sym) ⇒ Boolean
17 18 19 |
# File 'lib/aspera/schema/registry.rb', line 17 def known?(sym) LOCATIONS.key?(sym) end |
.owned?(name_path) ⇒ Boolean
Returns true if schema is owned by ascli (not a vendor API).
23 24 25 |
# File 'lib/aspera/schema/registry.rb', line 23 def owned?(name_path) OWNED.include?(name_path.split(':', 2).first.to_sym) end |
.query_params(component, endpoint, method: 'get') ⇒ String
Get path to query parameters for a GET endpoint
50 51 52 53 |
# File 'lib/aspera/schema/registry.rb', line 50 def query_params(component, endpoint, method: 'get') key, prefix = split_component(component) "#{key}:paths.#{prefix}/#{endpoint}.#{method}#{QUERY_PARAMS_SUFFIX}" end |
.req_body(component, endpoint) ⇒ String
Get path to request body schema, no check if it exists
40 41 42 43 |
# File 'lib/aspera/schema/registry.rb', line 40 def req_body(component, endpoint) key, prefix = split_component(component) "#{key}:paths.#{prefix}/#{endpoint}.requestBody.content.application/json.schema" end |
.split_component(component) ⇒ Array(String, String)
Split a component string into registry key and optional path prefix. Syntax: 'key' or 'key+/prefix' (e.g. 'shares+/api/v1')
31 32 33 34 |
# File 'lib/aspera/schema/registry.rb', line 31 def split_component(component) key, prefix = component.split('+', 2) [key, prefix || ''] end |
Instance Method Details
#reader(name_path) ⇒ Reader
Read schema from file or from cache.
When name_path ends with QUERY_PARAMS_SUFFIX, the OAS parameters array at that path
is synthesised into an object schema via Reader.from_query_params instead of navigating
into the tree.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/aspera/schema/registry.rb', line 116 def reader(name_path) name, path = name_path.split(':', 2) sym = name.to_sym Aspera.assert(Registry.known?(sym)) { "schema: #{sym}" } spec_file = File.join(@main_folder, LOCATIONS[sym]) @cache[sym] = Yaml.safe_load(File.read(spec_file)) if spec_file.end_with?('.yaml') && !@cache.key?(sym) @cache[sym] = JSON.parse(File.read(spec_file)) if spec_file.end_with?('.json') && !@cache.key?(sym) # Query-params path: strip the suffix, navigate to the operation node, extract parameters if path&.end_with?(QUERY_PARAMS_SUFFIX) parent_path = path.delete_suffix(QUERY_PARAMS_SUFFIX) node = @cache[sym].dig(*parent_path.split('.')) # Parameters may be references to components.parameters params = (node&.fetch('parameters', []) || []).map do |param| param.key?('$ref') ? @cache[sym].dig(*param['$ref'].delete_prefix('#/').split('/')) : param end return Reader.from_query_params(params.compact) end reader = Reader.new(@cache[sym]) return reader unless path reader.dig(*path.split('.')) end |