scorpio 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/LICENSE.md +1 -1
- data/README.md +26 -17
- data/lib/scorpio/google_api_document.rb +9 -1
- data/lib/scorpio/openapi/document.rb +4 -2
- data/lib/scorpio/openapi/operation.rb +90 -40
- data/lib/scorpio/openapi/operations_scope.rb +13 -11
- data/lib/scorpio/openapi/reference.rb +27 -2
- data/lib/scorpio/openapi/tag.rb +15 -0
- data/lib/scorpio/openapi/v3/server.rb +3 -1
- data/lib/scorpio/openapi.rb +55 -17
- data/lib/scorpio/pickle_adapter.rb +2 -0
- data/lib/scorpio/request.rb +47 -32
- data/lib/scorpio/resource_base.rb +234 -201
- data/lib/scorpio/response.rb +6 -4
- data/lib/scorpio/ur.rb +7 -3
- data/lib/scorpio/version.rb +3 -1
- data/lib/scorpio.rb +5 -6
- data/scorpio.gemspec +15 -23
- metadata +21 -220
- data/.simplecov +0 -1
- data/Rakefile +0 -10
- data/bin/documents_to_yml.rb +0 -33
- data/resources/icons/AGPL-3.0.png +0 -0
- data/test/blog.openapi2.yml +0 -113
- data/test/blog.openapi3.yml +0 -131
- data/test/blog.rb +0 -117
- data/test/blog.rest_description.yml +0 -67
- data/test/blog_scorpio_models.rb +0 -49
- data/test/scorpio_test.rb +0 -105
- data/test/test_helper.rb +0 -86
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b089f79e383e48c4d73793ceb0b1f3a3a254aca6abc34176252d24828ca0a9d8
|
4
|
+
data.tar.gz: aa9f34563eacae7e07c68f757a66bc53504dbdde741c285f40b34f15a64a08a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d87134df5c11b0157c861fddd25947d3f45cbd2b17c29d9de23d22e8dd70e4edc2b6b1378ef3f5cfee0e0bff7872e9179e76b5f97e824d14a231ef19fd068cd
|
7
|
+
data.tar.gz: f82dd97cdb8a8b2f0912d1d5e9a8198f5f7734dcb46c914b0c9cb25a3763d0c0a9d4c0c690a3598d53a8c62f065fde18dec0c42586f2257da289b776d091a3c2
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
# v0.6.0
|
2
|
+
- ResourceBase resources in responses may be contained anywhere in the response object contained in ResourceBase::Container
|
3
|
+
- ResourceBase defines class methods for operations whose request or response schemas are represented by the resource
|
4
|
+
- JSI v0.6.0
|
5
|
+
|
1
6
|
# v0.5.0
|
2
7
|
- JSI schema classes change to schema modules, JSI instances may have multiple schemas (JSI v0.4, Ur v0.2)
|
3
8
|
|
data/LICENSE.md
CHANGED
@@ -2,7 +2,7 @@ Copright © [Ethan](https://github.com/notEthan/)
|
|
2
2
|
|
3
3
|
[<img align="right" src="https://github.com/notEthan/scorpio/raw/master/resources/icons/AGPL-3.0.png">](https://www.gnu.org/licenses/agpl-3.0.html)
|
4
4
|
|
5
|
-
Scorpio is
|
5
|
+
Scorpio is licensed under the terms of the [GNU Affero General Public License version 3](https://www.gnu.org/licenses/agpl-3.0.html).
|
6
6
|
|
7
7
|
GNU Affero General Public License
|
8
8
|
=================================
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Scorpio
|
2
2
|
|
3
|
-
|
3
|
+
![Test CI Status](https://github.com/notEthan/scorpio/actions/workflows/test.yml/badge.svg?branch=stable)
|
4
4
|
[![Coverage Status](https://coveralls.io/repos/github/notEthan/scorpio/badge.svg)](https://coveralls.io/github/notEthan/scorpio)
|
5
5
|
|
6
6
|
Scorpio is a library that helps you, as a client, consume an HTTP service described by an OpenAPI document. You provide the OpenAPI description document, a little bit of configuration, and Scorpio will take that and dynamically generate an interface for you to call the service's operations and interact with its resources as an ORM.
|
@@ -24,7 +24,7 @@ Once you have the OpenAPI document describing the service you will consume, you
|
|
24
24
|
|
25
25
|
## Pet Store (using Scorpio::ResourceBase)
|
26
26
|
|
27
|
-
Let's dive into some code, shall we? If you have learned about OpenAPI, you likely learned using the example of the Pet Store service. This README will use the same service. Its documentation is at
|
27
|
+
Let's dive into some code, shall we? If you have learned about OpenAPI, you likely learned using the example of the Pet Store service. This README will use the same service. Its documentation is at https://petstore.swagger.io/.
|
28
28
|
|
29
29
|
Using the OpenAPI document, we can start interacting with the pet store with very little code. Here is that code, with explanations of each part in the comments.
|
30
30
|
|
@@ -42,7 +42,7 @@ module PetStore
|
|
42
42
|
# (making network calls at application boot time is usually a bad idea), but for this
|
43
43
|
# example we will do a quick-and-dirty http get.
|
44
44
|
require 'json'
|
45
|
-
self.openapi_document = JSON.parse(Faraday.get('
|
45
|
+
self.openapi_document = JSON.parse(Faraday.get('https://petstore.swagger.io/v2/swagger.json').body)
|
46
46
|
end
|
47
47
|
|
48
48
|
# a Pet is a resource of the pet store, so inherits from PetStore::Resource
|
@@ -67,7 +67,8 @@ end
|
|
67
67
|
That should be all you need to start calling operations:
|
68
68
|
|
69
69
|
```ruby
|
70
|
-
# call the operation findPetsByStatus
|
70
|
+
# call the operation findPetsByStatus
|
71
|
+
# doc: https://petstore.swagger.io/#/pet/findPetsByStatus
|
71
72
|
sold_pets = PetStore::Pet.findPetsByStatus(status: 'sold')
|
72
73
|
# sold_pets is an array-like collection of PetStore::Pet instances
|
73
74
|
|
@@ -79,7 +80,7 @@ pet.tags.map(&:name)
|
|
79
80
|
# (your tag names will be different depending on what's in the pet store)
|
80
81
|
# => ["aucune"]
|
81
82
|
|
82
|
-
# compare to getPetById:
|
83
|
+
# compare to getPetById: https://petstore.swagger.io/#/pet/getPetById
|
83
84
|
pet == PetStore::Pet.getPetById(petId: pet['id'])
|
84
85
|
# pet is the same, retrieved using the getPetById operation
|
85
86
|
|
@@ -89,7 +90,7 @@ pet.name = ENV['USER']
|
|
89
90
|
# store the result in the pet store. note the updatePet call from the instance - our
|
90
91
|
# calls so far have been on the class PetStore::Pet, but scorpio defines instance
|
91
92
|
# methods to call operations where appropriate as well.
|
92
|
-
# updatePet:
|
93
|
+
# updatePet: https://petstore.swagger.io/#/pet/updatePet
|
93
94
|
pet.updatePet
|
94
95
|
|
95
96
|
# check that it was saved
|
@@ -107,13 +108,13 @@ Isn't that cool? You get class methods like getPetById, instance methods like up
|
|
107
108
|
|
108
109
|
## Pet Store (using Scorpio::OpenAPI classes)
|
109
110
|
|
110
|
-
You do not have to define resource classes to use Scorpio to call OpenAPI operations - the classes Scorpio uses to represent concepts from OpenAPI can be called directly. Scorpio uses [JSI](https://github.com/notEthan/
|
111
|
+
You do not have to define resource classes to use Scorpio to call OpenAPI operations - the classes Scorpio uses to represent concepts from OpenAPI can be called directly. Scorpio uses [JSI](https://github.com/notEthan/jsi) classes to represent OpenAPI schemes such as the Document and its Operations.
|
111
112
|
|
112
113
|
We start by instantiating the OpenAPI document. `Scorpio::OpenAPI::Document.from_instance` returns a V2 or V3 OpenAPI Document class instance.
|
113
114
|
|
114
115
|
```ruby
|
115
116
|
require 'scorpio'
|
116
|
-
pet_store_doc = Scorpio::OpenAPI::Document.from_instance(JSON.parse(Faraday.get('
|
117
|
+
pet_store_doc = Scorpio::OpenAPI::Document.from_instance(JSON.parse(Faraday.get('https://petstore.swagger.io/v2/swagger.json').body))
|
117
118
|
# => #{<Scorpio::OpenAPI::V2::Document fragment="#"> "swagger" => "2.0", ...}
|
118
119
|
```
|
119
120
|
|
@@ -122,7 +123,7 @@ The OpenAPI document holds the JSON that represents it, so to get an Operation y
|
|
122
123
|
```ruby
|
123
124
|
# the store inventory operation will let us see what statuses there are in the store.
|
124
125
|
inventory_op = pet_store_doc.paths['/store/inventory']['get']
|
125
|
-
# => #{<Scorpio::OpenAPI::V2::Operation
|
126
|
+
# => #{<JSI (Scorpio::OpenAPI::V2::Operation)>
|
126
127
|
# "summary" => "Returns pet inventories by status",
|
127
128
|
# "operationId" => "getInventory",
|
128
129
|
# ...
|
@@ -140,7 +141,7 @@ Now that we have an operation, we can run requests from it. {Scorpio::OpenAPI::O
|
|
140
141
|
|
141
142
|
```ruby
|
142
143
|
inventory = inventory_op.run
|
143
|
-
# => #{<JSI
|
144
|
+
# => #{<JSI>
|
144
145
|
# "unavailable" => 4,
|
145
146
|
# "unloved - needs a home" => 1,
|
146
147
|
# "available" => 2350,
|
@@ -152,28 +153,36 @@ inventory = inventory_op.run
|
|
152
153
|
let's pick a state and find a pet. we'll go through the rest of the example in the ResourceBase section pretty much like it is up there:
|
153
154
|
|
154
155
|
```ruby
|
155
|
-
# call the operation findPetsByStatus
|
156
|
+
# call the operation findPetsByStatus
|
157
|
+
# doc: https://petstore.swagger.io/#/pet/findPetsByStatus
|
156
158
|
sold_pets = pet_store_doc.operations['findPetsByStatus'].run(status: 'sold')
|
157
159
|
# sold_pets is an array-like collection of JSI instances
|
158
160
|
|
159
161
|
pet = sold_pets.detect { |pet| pet.tags.any? }
|
160
162
|
|
161
163
|
pet.tags.map(&:name)
|
162
|
-
# note that you have accessors on
|
164
|
+
# note that you have accessors on the returned JSI like #tags, and also that
|
163
165
|
# tags have accessors for properties 'name' and 'id' from the tags schema
|
164
166
|
# (your tag names will be different depending on what's in the pet store)
|
165
167
|
# => ["aucune"]
|
166
168
|
|
167
|
-
# compare to
|
168
|
-
|
169
|
+
# compare the pet from findPetsByStatus to one returned from getPetById
|
170
|
+
# doc: https://petstore.swagger.io/#/pet/getPetById
|
171
|
+
pet_by_id = pet_store_doc.operations['getPetById'].run(petId: pet['id'])
|
172
|
+
|
173
|
+
# unlike ResourceBase instances above, JSI instances have stricter
|
174
|
+
# equality and the pets returned from different operations are not
|
175
|
+
# equal, though the underlying JSON instance is.
|
176
|
+
pet_by_id == pet
|
169
177
|
# => false
|
170
|
-
|
178
|
+
pet_by_id.jsi_instance == pet.jsi_instance
|
179
|
+
# => true
|
171
180
|
|
172
181
|
# let's name the pet after ourself
|
173
182
|
pet.name = ENV['USER']
|
174
183
|
|
175
184
|
# store the result in the pet store.
|
176
|
-
# updatePet:
|
185
|
+
# updatePet: https://petstore.swagger.io/#/pet/updatePet
|
177
186
|
pet_store_doc.operations['updatePet'].run(body_object: pet)
|
178
187
|
|
179
188
|
# check that it was saved
|
@@ -218,7 +227,7 @@ When these are set, Scorpio::ResourceBase looks through the API description and
|
|
218
227
|
If you need a more complete representation of the HTTP request and/or response, Scorpio::OpenAPI::Operation#run_ur or Scorpio::Request#run_ur will return a representation of the request and response defined by the gem [Ur](https://github.com/notEthan/ur). See that link for more detail. Relating to the example above titled "Pet Store (using Scorpio::OpenAPI classes)", this code will return an Ur:
|
219
228
|
|
220
229
|
```ruby
|
221
|
-
inventory_op = Scorpio::OpenAPI::Document.from_instance(JSON.parse(Faraday.get('
|
230
|
+
inventory_op = Scorpio::OpenAPI::Document.from_instance(JSON.parse(Faraday.get('https://petstore.swagger.io/v2/swagger.json').body)).paths['/store/inventory']['get']
|
222
231
|
inventory_ur = inventory_op.run_ur
|
223
232
|
# => #{<Scorpio::Ur fragment="#"> ...}
|
224
233
|
```
|
@@ -1,7 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module Google
|
3
5
|
discovery_rest_description_doc = ::JSON.parse(Scorpio.root.join('documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest').read)
|
4
|
-
discovery_rest_description = JSI::MetaschemaNode.new(
|
6
|
+
discovery_rest_description = JSI::MetaschemaNode.new(
|
7
|
+
discovery_rest_description_doc,
|
8
|
+
metaschema_root_ptr: JSI::Ptr['schemas']['JsonSchema'],
|
9
|
+
root_schema_ptr: JSI::Ptr['schemas']['RestDescription'],
|
10
|
+
metaschema_instance_modules: [JSI::Schema::Draft04],
|
11
|
+
)
|
5
12
|
|
6
13
|
# naming these is not strictly necessary, but is nice to have.
|
7
14
|
DirectoryList = discovery_rest_description.schemas['DirectoryList'].jsi_schema_module
|
@@ -165,6 +172,7 @@ module Scorpio
|
|
165
172
|
'schemes' => ad.rootUrl ? [Addressable::URI.parse(ad.rootUrl).scheme] : ad.baseUrl ? [Addressable::URI.parse(ad.rootUrl).scheme] : [], #/definitions/schemesList
|
166
173
|
'consumes' => ['application/json'], # we'll just make this assumption
|
167
174
|
'produces' => ['application/json'],
|
175
|
+
'tags' => paths.flat_map { |_, p| p.flat_map { |_, op| (op['tags'] || []).map { |n| {'name' => n} } } }.uniq,
|
168
176
|
'paths' => paths, #/definitions/paths
|
169
177
|
}
|
170
178
|
if ad.schemas
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
# A document that defines or describes an API.
|
@@ -52,7 +54,7 @@ module Scorpio
|
|
52
54
|
attr_writer :faraday_adapter
|
53
55
|
def faraday_adapter
|
54
56
|
return @faraday_adapter if instance_variable_defined?(:@faraday_adapter)
|
55
|
-
[Faraday.default_adapter]
|
57
|
+
[Faraday.default_adapter].freeze
|
56
58
|
end
|
57
59
|
|
58
60
|
attr_writer :logger
|
@@ -157,7 +159,7 @@ module Scorpio
|
|
157
159
|
scheme: scheme,
|
158
160
|
host: host,
|
159
161
|
path: basePath,
|
160
|
-
).
|
162
|
+
).freeze
|
161
163
|
end
|
162
164
|
end
|
163
165
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
# An OpenAPI operation
|
@@ -43,37 +45,42 @@ module Scorpio
|
|
43
45
|
end
|
44
46
|
include Configurables
|
45
47
|
|
46
|
-
#
|
48
|
+
# openapi v3?
|
49
|
+
# @return [Boolean]
|
47
50
|
def v3?
|
48
51
|
is_a?(V3::Operation)
|
49
52
|
end
|
50
53
|
|
51
|
-
#
|
54
|
+
# openapi v2?
|
55
|
+
# @return [Boolean]
|
52
56
|
def v2?
|
53
57
|
is_a?(V2::Operation)
|
54
58
|
end
|
55
59
|
|
56
|
-
#
|
60
|
+
# the document whence this operation came
|
61
|
+
# @return [Scorpio::OpenAPI::Document]
|
57
62
|
def openapi_document
|
58
|
-
|
63
|
+
jsi_parent_nodes.detect { |p| p.is_a?(Scorpio::OpenAPI::Document) }
|
59
64
|
end
|
60
65
|
|
66
|
+
# @return [String]
|
61
67
|
def path_template_str
|
62
68
|
return @path_template_str if instance_variable_defined?(:@path_template_str)
|
63
|
-
raise(Bug) unless
|
64
|
-
raise(Bug) unless
|
65
|
-
@path_template_str =
|
69
|
+
raise(Bug) unless jsi_parent_node.is_a?(Scorpio::OpenAPI::V2::PathItem) || jsi_parent_node.is_a?(Scorpio::OpenAPI::V3::PathItem)
|
70
|
+
raise(Bug) unless jsi_parent_node.jsi_parent_node.is_a?(Scorpio::OpenAPI::V2::Paths) || jsi_parent_node.jsi_parent_node.is_a?(Scorpio::OpenAPI::V3::Paths)
|
71
|
+
@path_template_str = jsi_parent_node.jsi_ptr.tokens.last
|
66
72
|
end
|
67
73
|
|
68
|
-
#
|
74
|
+
# the path as an Addressable::Template
|
75
|
+
# @return [Addressable::Template]
|
69
76
|
def path_template
|
70
77
|
return @path_template if instance_variable_defined?(:@path_template)
|
71
78
|
@path_template = Addressable::Template.new(path_template_str)
|
72
79
|
end
|
73
80
|
|
81
|
+
# the URI template, consisting of the base_url concatenated with the path template
|
74
82
|
# @param base_url [#to_str] the base URL to which the path template is appended
|
75
|
-
# @return [Addressable::Template]
|
76
|
-
# concatenated with the path template
|
83
|
+
# @return [Addressable::Template]
|
77
84
|
def uri_template(base_url: self.base_url)
|
78
85
|
unless base_url
|
79
86
|
raise(ArgumentError, "no base_url has been specified for operation #{self}")
|
@@ -83,33 +90,38 @@ module Scorpio
|
|
83
90
|
Addressable::Template.new(File.join(base_url, path_template_str))
|
84
91
|
end
|
85
92
|
|
86
|
-
#
|
87
|
-
#
|
93
|
+
# the HTTP method of this operation as indicated by the attribute name for this operation
|
94
|
+
# from the parent PathItem
|
95
|
+
# @return [String]
|
88
96
|
def http_method
|
89
97
|
return @http_method if instance_variable_defined?(:@http_method)
|
90
|
-
raise(Bug) unless
|
91
|
-
@http_method = jsi_ptr.
|
98
|
+
raise(Bug) unless jsi_parent_node.is_a?(Scorpio::OpenAPI::V2::PathItem) || jsi_parent_node.is_a?(Scorpio::OpenAPI::V3::PathItem)
|
99
|
+
@http_method = jsi_ptr.tokens.last
|
92
100
|
end
|
93
101
|
|
94
|
-
#
|
102
|
+
# a short identifier for this operation appropriate for an error message
|
103
|
+
# @return [String]
|
95
104
|
def human_id
|
96
105
|
operationId || "path: #{path_template_str}, method: #{http_method}"
|
97
106
|
end
|
98
107
|
|
108
|
+
# @param status [String, Integer]
|
99
109
|
# @return [Scorpio::OpenAPI::V3::Response, Scorpio::OpenAPI::V2::Response]
|
100
110
|
def oa_response(status: )
|
101
111
|
status = status.to_s if status.is_a?(Numeric)
|
102
|
-
if
|
103
|
-
_, oa_response =
|
104
|
-
oa_response ||=
|
112
|
+
if responses
|
113
|
+
_, oa_response = responses.detect { |k, v| k.to_s == status }
|
114
|
+
oa_response ||= responses['default']
|
105
115
|
end
|
106
116
|
oa_response
|
107
117
|
end
|
108
118
|
|
119
|
+
# the parameters specified for this operation, plus any others scorpio considers to be parameters.
|
120
|
+
#
|
109
121
|
# this method is not intended to be API-stable at the moment.
|
110
122
|
#
|
111
|
-
# @
|
112
|
-
#
|
123
|
+
# @api private
|
124
|
+
# @return [#to_ary<#to_h>]
|
113
125
|
def inferred_parameters
|
114
126
|
parameters = self.parameters ? self.parameters.to_a.dup : []
|
115
127
|
path_template.variables.each do |var|
|
@@ -127,7 +139,8 @@ module Scorpio
|
|
127
139
|
parameters
|
128
140
|
end
|
129
141
|
|
130
|
-
#
|
142
|
+
# a module with accessor methods for unambiguously named parameters of this operation.
|
143
|
+
# @return [Module]
|
131
144
|
def request_accessor_module
|
132
145
|
return @request_accessor_module if instance_variable_defined?(:@request_accessor_module)
|
133
146
|
@request_accessor_module = begin
|
@@ -136,7 +149,7 @@ module Scorpio
|
|
136
149
|
instance_method_modules = [Request, Request::Configurables]
|
137
150
|
instance_method_names = instance_method_modules.map do |mod|
|
138
151
|
(mod.instance_methods + mod.private_instance_methods).map(&:to_s)
|
139
|
-
end.inject(Set.new,
|
152
|
+
end.inject(Set.new, &:merge)
|
140
153
|
params_by_name.each do |name, params|
|
141
154
|
next if instance_method_names.include?(name)
|
142
155
|
if params.size == 1
|
@@ -149,19 +162,22 @@ module Scorpio
|
|
149
162
|
end
|
150
163
|
end
|
151
164
|
|
152
|
-
#
|
165
|
+
# instantiates a {Scorpio::Request} for this operation.
|
166
|
+
# parameters are all passed to {Scorpio::Request#initialize}.
|
153
167
|
# @return [Scorpio::Request]
|
154
168
|
def build_request(*a, &b)
|
155
169
|
Scorpio::Request.new(self, *a, &b)
|
156
170
|
end
|
157
171
|
|
158
|
-
#
|
172
|
+
# runs a {Scorpio::Request} for this operation, returning a {Scorpio::Ur}.
|
173
|
+
# parameters are all passed to {Scorpio::Request#initialize}.
|
159
174
|
# @return [Scorpio::Ur] response ur
|
160
175
|
def run_ur(*a, &b)
|
161
176
|
build_request(*a, &b).run_ur
|
162
177
|
end
|
163
178
|
|
164
|
-
#
|
179
|
+
# runs a {Scorpio::Request} for this operation - see {Scorpio::Request#run}.
|
180
|
+
# parameters are all passed to {Scorpio::Request#initialize}.
|
165
181
|
# @return response body object
|
166
182
|
def run(*a, &b)
|
167
183
|
build_request(*a, &b).run
|
@@ -212,17 +228,19 @@ module Scorpio
|
|
212
228
|
requestBody['content'] &&
|
213
229
|
requestBody['content'][media_type] &&
|
214
230
|
requestBody['content'][media_type]['schema']
|
215
|
-
schema_object ? JSI::Schema.
|
231
|
+
schema_object ? JSI::Schema.ensure_schema(schema_object) : nil
|
216
232
|
end
|
217
233
|
|
218
|
-
# @return [
|
234
|
+
# @return [JSI::SchemaSet]
|
219
235
|
def request_schemas
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
236
|
+
JSI::SchemaSet.build do |schemas|
|
237
|
+
if requestBody && requestBody['content']
|
238
|
+
requestBody['content'].each_value do |oa_media_type|
|
239
|
+
if oa_media_type['schema']
|
240
|
+
schemas << oa_media_type['schema']
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
226
244
|
end
|
227
245
|
end
|
228
246
|
|
@@ -232,7 +250,24 @@ module Scorpio
|
|
232
250
|
oa_media_types = oa_response ? oa_response['content'] : nil # Scorpio::OpenAPI::V3::MediaTypes
|
233
251
|
oa_media_type = oa_media_types ? oa_media_types[media_type] : nil # Scorpio::OpenAPI::V3::MediaType
|
234
252
|
oa_schema = oa_media_type ? oa_media_type['schema'] : nil # Scorpio::OpenAPI::V3::Schema
|
235
|
-
oa_schema ? JSI::Schema.
|
253
|
+
oa_schema ? JSI::Schema.ensure_schema(oa_schema) : nil
|
254
|
+
end
|
255
|
+
|
256
|
+
# @return [JSI::SchemaSet]
|
257
|
+
def response_schemas
|
258
|
+
JSI::SchemaSet.build do |schemas|
|
259
|
+
if responses
|
260
|
+
responses.each_value do |oa_response|
|
261
|
+
if oa_response['content']
|
262
|
+
oa_response['content'].each_value do |oa_media_type|
|
263
|
+
if oa_media_type['schema']
|
264
|
+
schemas << oa_media_type['schema']
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
236
271
|
end
|
237
272
|
end
|
238
273
|
end
|
@@ -264,7 +299,8 @@ module Scorpio
|
|
264
299
|
end
|
265
300
|
include Configurables
|
266
301
|
|
267
|
-
#
|
302
|
+
# the body parameter
|
303
|
+
# @return [#to_hash]
|
268
304
|
# @raise [Scorpio::OpenAPI::SemanticError] if there's more than one body param
|
269
305
|
def body_parameter
|
270
306
|
body_parameters = (parameters || []).select { |parameter| parameter['in'] == 'body' }
|
@@ -278,19 +314,20 @@ module Scorpio
|
|
278
314
|
end
|
279
315
|
end
|
280
316
|
|
317
|
+
# request schema for the given media_type
|
281
318
|
# @param media_type unused
|
282
|
-
# @return [JSI::Schema]
|
319
|
+
# @return [JSI::Schema]
|
283
320
|
def request_schema(media_type: nil)
|
284
321
|
if body_parameter && body_parameter['schema']
|
285
|
-
JSI::Schema.
|
322
|
+
JSI::Schema.ensure_schema(body_parameter['schema'])
|
286
323
|
else
|
287
324
|
nil
|
288
325
|
end
|
289
326
|
end
|
290
327
|
|
291
|
-
# @return [
|
328
|
+
# @return [JSI::SchemaSet]
|
292
329
|
def request_schemas
|
293
|
-
request_schema ? [request_schema] : []
|
330
|
+
request_schema ? JSI::SchemaSet[request_schema] : JSI::SchemaSet[]
|
294
331
|
end
|
295
332
|
|
296
333
|
# @param status [Integer, String] response status
|
@@ -299,7 +336,20 @@ module Scorpio
|
|
299
336
|
def response_schema(status: , media_type: nil)
|
300
337
|
oa_response = self.oa_response(status: status)
|
301
338
|
oa_response_schema = oa_response ? oa_response['schema'] : nil # Scorpio::OpenAPI::V2::Schema
|
302
|
-
oa_response_schema ? JSI::Schema.
|
339
|
+
oa_response_schema ? JSI::Schema.ensure_schema(oa_response_schema) : nil
|
340
|
+
end
|
341
|
+
|
342
|
+
# @return [JSI::SchemaSet]
|
343
|
+
def response_schemas
|
344
|
+
JSI::SchemaSet.build do |schemas|
|
345
|
+
if responses
|
346
|
+
responses.each_value do |oa_response|
|
347
|
+
if oa_response['schema']
|
348
|
+
schemas << oa_response['schema']
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
303
353
|
end
|
304
354
|
end
|
305
355
|
end
|
@@ -1,13 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
# OperationsScope acts as an Enumerable of the Operations for an openapi_document,
|
4
6
|
# and offers subscripting by operationId.
|
5
7
|
class OperationsScope
|
6
|
-
include JSI::Util::Memoize
|
7
|
-
|
8
8
|
# @param openapi_document [Scorpio::OpenAPI::Document]
|
9
9
|
def initialize(openapi_document)
|
10
10
|
@openapi_document = openapi_document
|
11
|
+
@operations_by_id = Hash.new do |h, operationId|
|
12
|
+
op = detect { |operation| operation.operationId == operationId }
|
13
|
+
unless op
|
14
|
+
raise(::KeyError, "operationId not found: #{operationId.inspect}")
|
15
|
+
end
|
16
|
+
h[operationId] = op
|
17
|
+
end
|
11
18
|
end
|
12
19
|
attr_reader :openapi_document
|
13
20
|
|
@@ -23,17 +30,12 @@ module Scorpio
|
|
23
30
|
end
|
24
31
|
include Enumerable
|
25
32
|
|
26
|
-
#
|
27
|
-
# @
|
33
|
+
# finds an operation with the given `operationId`
|
34
|
+
# @param operationId [String] the operationId of the operation to find
|
35
|
+
# @return [Scorpio::OpenAPI::Operation]
|
28
36
|
# @raise [::KeyError] if the given operationId does not exist
|
29
37
|
def [](operationId)
|
30
|
-
|
31
|
-
detect { |operation| operation.operationId == operationId_ }.tap do |op|
|
32
|
-
unless op
|
33
|
-
raise(::KeyError, "operationId not found: #{operationId_.inspect}")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
38
|
+
@operations_by_id[operationId]
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
@@ -1,11 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
module Reference
|
4
6
|
# overrides JSI::Base#[] to implicitly dereference this Reference, except when
|
5
7
|
# the given token is present in this Reference's instance (this should usually
|
6
8
|
# only apply to the token '$ref')
|
7
|
-
#
|
8
|
-
# see JSI::Base#initialize documentation at https://www.rubydoc.info/gems/jsi/JSI/Base
|
9
9
|
def [](token, *a, &b)
|
10
10
|
if respond_to?(:to_hash) && !key?(token)
|
11
11
|
deref do |deref_jsi|
|
@@ -14,6 +14,31 @@ module Scorpio
|
|
14
14
|
end
|
15
15
|
return super
|
16
16
|
end
|
17
|
+
|
18
|
+
# yields or returns the target of this reference
|
19
|
+
# @yield [JSI::Base] if a block is given
|
20
|
+
# @return [JSI::Base]
|
21
|
+
def deref
|
22
|
+
return unless respond_to?(:to_hash) && self['$ref'].respond_to?(:to_str)
|
23
|
+
|
24
|
+
ref_uri = Addressable::URI.parse(self['$ref'])
|
25
|
+
ref_uri_nofrag = ref_uri.merge(fragment: nil)
|
26
|
+
|
27
|
+
if !ref_uri_nofrag.empty? || ref_uri.fragment.nil?
|
28
|
+
raise(NotImplementedError,
|
29
|
+
"Scorpio currently only supports fragment URIs as OpenAPI references. cannot find reference by uri: #{self['$ref']}"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
ptr = JSI::Ptr.from_fragment(ref_uri.fragment)
|
34
|
+
deref_jsi = ptr.evaluate(jsi_root_node)
|
35
|
+
|
36
|
+
# TODO type check deref_jsi
|
37
|
+
|
38
|
+
yield deref_jsi if block_given?
|
39
|
+
|
40
|
+
deref_jsi
|
41
|
+
end
|
17
42
|
end
|
18
43
|
end
|
19
44
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Scorpio
|
2
|
+
module OpenAPI
|
3
|
+
module Tag
|
4
|
+
# operations in the openapi document which have a tag with this tag's name
|
5
|
+
# @return [Enumerable<Scorpio::OpenAPI::Operation>]
|
6
|
+
def operations
|
7
|
+
unless jsi_root_node.is_a?(OpenAPI::Document)
|
8
|
+
raise("Tag#operations cannot be used on a Tag that is not inside an OpenAPI document")
|
9
|
+
end
|
10
|
+
|
11
|
+
jsi_root_node.operations.select { |op| op.tags.respond_to?(:to_ary) && op.tags.include?(name) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Scorpio
|
2
4
|
module OpenAPI
|
3
5
|
module V3
|
@@ -34,7 +36,7 @@ module Scorpio
|
|
34
36
|
server_variables = given_server_variables
|
35
37
|
end
|
36
38
|
template = Addressable::Template.new(url)
|
37
|
-
template.expand(server_variables)
|
39
|
+
template.expand(server_variables).freeze
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|