diesel-api-dsl 0.1.3 → 0.1.5
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/Gemfile +4 -0
- data/diesel.gemspec +1 -1
- data/lib/diesel/api_builder.rb +82 -32
- data/lib/diesel/api_group.rb +3 -2
- data/lib/diesel/middleware/auth/api_key.rb +13 -1
- data/lib/diesel/middleware/auth/basic.rb +27 -0
- data/lib/diesel/middleware/auth/oauth2.rb +2 -2
- data/lib/diesel/middleware/convert_json_body.rb +15 -0
- data/lib/diesel/middleware/set_body_parameter.rb +5 -1
- data/lib/diesel/middleware/set_path_parameter.rb +2 -1
- data/lib/diesel/request_context.rb +7 -1
- data/lib/diesel/swagger/definition.rb +1 -1
- data/lib/diesel/swagger/operation.rb +3 -1
- data/lib/diesel/swagger/parser.rb +25 -9
- data/lib/diesel/uri.rb +36 -0
- data/lib/diesel/version.rb +1 -1
- data/spec/diesel/api_builder_spec.rb +9 -3
- data/spec/diesel/swagger/parser_spec.rb +14 -0
- data/spec/diesel_spec.rb +173 -56
- data/spec/files/github.json +186 -0
- data/spec/files/harvest.json +75 -0
- data/spec/files/mandrill.json +502 -0
- data/spec/files/uber.json +43 -0
- data/spec/fixtures/vcr_cassettes/github_checkAuthorization.yml +71 -0
- data/spec/fixtures/vcr_cassettes/github_getUser.yml +82 -0
- data/spec/fixtures/vcr_cassettes/harvest_invoiceList.yml +85 -0
- data/spec/fixtures/vcr_cassettes/mandrill_userPing.yml +44 -0
- data/spec/fixtures/vcr_cassettes/uber_getProducts.yml +60 -0
- data/spec/spec_helper.rb +1 -0
- metadata +28 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78e75164894839881d3adf47cef1575c25970140
|
4
|
+
data.tar.gz: 3ac675a70ef2bf4373a1e4a1b1601a68f8daaa65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eceaffc1b439c45851509eb72f4f1e109a16c01b7a8901c104f8c023818f337a43875ef40f77e273d9eb14f8f0fbb99049e0986853a07cfe3c7d3c6720981771
|
7
|
+
data.tar.gz: b7be5c3cfdb6bf224263dc13c02f3f0aeab75af8574364a0d9f44133ff3f6a1a77fd20f9d39a86ae060b988c5c5ae96dc717fa0eba3fa5303f5b72300f4be4cb
|
data/Gemfile
CHANGED
data/diesel.gemspec
CHANGED
data/lib/diesel/api_builder.rb
CHANGED
@@ -3,9 +3,11 @@ require 'diesel/api_error'
|
|
3
3
|
require 'diesel/utils/inflections'
|
4
4
|
require 'diesel/api_base'
|
5
5
|
require 'diesel/data_model'
|
6
|
+
require 'diesel/uri'
|
6
7
|
|
7
8
|
require 'diesel/middleware/debug'
|
8
9
|
require 'diesel/middleware/set_header'
|
10
|
+
require 'diesel/middleware/convert_json_body'
|
9
11
|
|
10
12
|
module Diesel
|
11
13
|
class APIBuilder
|
@@ -25,8 +27,8 @@ module Diesel
|
|
25
27
|
APIGroup.new.tap do |group|
|
26
28
|
group.endpoints = build_endpoints
|
27
29
|
group.endpoints.each do |endpoint|
|
28
|
-
klass.send(:define_method, endpoint.name.to_sym) do |
|
29
|
-
group.execute(self.options, endpoint,
|
30
|
+
klass.send(:define_method, endpoint.name.to_sym) do |*args|
|
31
|
+
group.execute(self.options, endpoint, *args)
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
@@ -50,15 +52,13 @@ module Diesel
|
|
50
52
|
end
|
51
53
|
|
52
54
|
def build_endpoint_url(specification, path)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
resource_path
|
61
|
-
].join('')
|
55
|
+
uri = Diesel::URI.new
|
56
|
+
uri.scheme = specification.schemes.first
|
57
|
+
uri.host = specification.host
|
58
|
+
uri.base_host = specification.extensions[:base_host]
|
59
|
+
uri.base_path = specification.base_path.chomp('/')
|
60
|
+
uri.resource_path = path.sub(/^([^\/])/, '/\1').chomp('/')
|
61
|
+
uri
|
62
62
|
end
|
63
63
|
|
64
64
|
def build_endpoints
|
@@ -72,30 +72,73 @@ module Diesel
|
|
72
72
|
|
73
73
|
Diesel::Endpoint.new(endpoint_name, endpoint_url, method).tap do |endpoint|
|
74
74
|
endpoint.config_middleware do
|
75
|
-
|
75
|
+
consumes =
|
76
|
+
(operation.consumes && operation.consumes.first) ||
|
77
|
+
(spec.consumes && spec.consumes.first)
|
78
|
+
|
79
|
+
produces =
|
80
|
+
(operation.produces && operation.produces.first) ||
|
81
|
+
(spec.produces && spec.produces.first)
|
76
82
|
|
83
|
+
security = operation.security || spec.security
|
84
|
+
|
85
|
+
use Diesel::Middleware::Debug
|
77
86
|
use Diesel::Middleware::SetHeader, 'User-Agent' => "diesel-rb/#{Diesel::VERSION}"
|
78
87
|
|
79
|
-
if
|
80
|
-
use Diesel::Middleware::SetHeader, 'Content-Type' =>
|
88
|
+
if consumes
|
89
|
+
use Diesel::Middleware::SetHeader, 'Content-Type' => consumes
|
81
90
|
end
|
82
91
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
if produces
|
93
|
+
use Diesel::Middleware::SetHeader, 'Accept' => produces
|
94
|
+
end
|
95
|
+
|
96
|
+
security_ids = if security && !security.empty?
|
97
|
+
security.keys
|
98
|
+
else
|
99
|
+
if operation.security_definitions && !operation.security_definitions.empty?
|
100
|
+
[operation.security_definitions.keys.first]
|
101
|
+
elsif spec.security_definitions && !spec.security_definitions.empty?
|
102
|
+
[spec.security_definitions.keys.first]
|
103
|
+
else
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
unless security_ids.empty?
|
109
|
+
security_defs = spec.security_definitions || {}
|
110
|
+
if operation.security_definitions
|
111
|
+
security_defs = security_defs.merge(operation.security_definitions)
|
112
|
+
end
|
113
|
+
|
114
|
+
security_ids.each do |name|
|
115
|
+
security_def = security_defs[name]
|
116
|
+
|
117
|
+
if security_def.nil?
|
118
|
+
raise APIError, "Missing security definition: #{name}, operation=#{endpoint_name}, method=#{method}"
|
119
|
+
end
|
120
|
+
|
121
|
+
case security_def.type
|
122
|
+
when 'apiKey'
|
123
|
+
require 'diesel/middleware/auth/api_key'
|
124
|
+
use Diesel::Middleware::Auth::APIKey,
|
125
|
+
id: Diesel::Utils::Inflections.underscore(name).to_sym,
|
126
|
+
in: security_def.in,
|
127
|
+
name: security_def.name,
|
128
|
+
format: security_def.extensions[:format]
|
129
|
+
when 'oauth2'
|
130
|
+
require 'diesel/middleware/auth/oauth2'
|
131
|
+
use Diesel::Middleware::Auth::OAuth2,
|
132
|
+
id: Diesel::Utils::Inflections.underscore(name).to_sym,
|
133
|
+
in: security_def.in,
|
134
|
+
name: security_def.name
|
135
|
+
when "basic"
|
136
|
+
require "diesel/middleware/auth/basic"
|
137
|
+
use Diesel::Middleware::Auth::Basic,
|
138
|
+
id: Diesel::Utils::Inflections.underscore(name).to_sym
|
139
|
+
else
|
140
|
+
raise APIError, "Unsupported security definition: #{security_def.type}"
|
141
|
+
end
|
99
142
|
end
|
100
143
|
end
|
101
144
|
|
@@ -106,14 +149,21 @@ module Diesel
|
|
106
149
|
param_class = Diesel::Utils::Inflections.constantize(param_class_name)
|
107
150
|
middleware_opts = {name: parameter.name}
|
108
151
|
if parameter.schema?
|
109
|
-
|
110
|
-
|
152
|
+
schema = parameter.schema
|
153
|
+
if schema.kind_of? String
|
154
|
+
schema = data_models[parameter.schema]
|
155
|
+
end
|
156
|
+
unless schema
|
157
|
+
raise APIError, "Unspecified schema: #{parameter.schema}; parameter=#{parameter.name}"
|
111
158
|
end
|
112
159
|
middleware_opts[:schema] = schema
|
113
160
|
end
|
114
161
|
use param_class, middleware_opts
|
115
162
|
end
|
116
163
|
|
164
|
+
if consumes == "application/json"
|
165
|
+
use Diesel::Middleware::ConvertJSONBody
|
166
|
+
end
|
117
167
|
end
|
118
168
|
end
|
119
169
|
end
|
data/lib/diesel/api_group.rb
CHANGED
@@ -20,8 +20,9 @@ module Diesel
|
|
20
20
|
@data_models = data_models
|
21
21
|
end
|
22
22
|
|
23
|
-
def execute(options, endpoint, parameters)
|
24
|
-
|
23
|
+
def execute(options, endpoint, *parameters)
|
24
|
+
params = parameters.last || {}
|
25
|
+
RequestContext.new(options, self, endpoint, params).perform
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -8,19 +8,31 @@ module Diesel
|
|
8
8
|
@id = options[:id]
|
9
9
|
@in = options[:in]
|
10
10
|
@name = options[:name]
|
11
|
+
@format = options[:format]
|
11
12
|
end
|
12
13
|
|
13
14
|
def call(env)
|
14
15
|
context = env[:context]
|
15
|
-
value = context.options[@id]
|
16
|
+
value = format_value(context.options[@id])
|
16
17
|
if @in == :header
|
17
18
|
env[:request_headers][@name] = value
|
18
19
|
elsif @in == :query
|
19
20
|
env[:params][@name] = value
|
21
|
+
elsif @in == :body
|
22
|
+
env[:body] = if body = env[:body]
|
23
|
+
body.merge(@name => value)
|
24
|
+
else
|
25
|
+
{ @name => value }
|
26
|
+
end
|
20
27
|
end
|
21
28
|
@app.call(env)
|
22
29
|
end
|
23
30
|
|
31
|
+
protected
|
32
|
+
def format_value(val)
|
33
|
+
return val unless @format
|
34
|
+
@format % val
|
35
|
+
end
|
24
36
|
end
|
25
37
|
end
|
26
38
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Diesel
|
2
|
+
module Middleware
|
3
|
+
module Auth
|
4
|
+
class Basic
|
5
|
+
|
6
|
+
AUTHORIZATION_HEADER = 'Authorization'.freeze
|
7
|
+
|
8
|
+
def initialize(app, options)
|
9
|
+
@app = app
|
10
|
+
@id = options[:id]
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
context = env[:context]
|
15
|
+
auth_options = context.options[@id]
|
16
|
+
username = auth_options[:username]
|
17
|
+
password = auth_options[:password]
|
18
|
+
value = Base64.encode64([username, password].join(':'))
|
19
|
+
value.gsub!("\n", '')
|
20
|
+
env[:request_headers][AUTHORIZATION_HEADER] = "Basic #{value}"
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -3,8 +3,8 @@ module Diesel
|
|
3
3
|
module Auth
|
4
4
|
class OAuth2
|
5
5
|
|
6
|
-
AUTHORIZATION_HEADER = 'Authorization'
|
7
|
-
AUTHORIZATION_HEADER_FORMAT = 'Bearer %s'
|
6
|
+
AUTHORIZATION_HEADER = 'Authorization'.freeze
|
7
|
+
AUTHORIZATION_HEADER_FORMAT = 'Bearer %s'.freeze
|
8
8
|
|
9
9
|
def initialize(app, options)
|
10
10
|
@app = app
|
@@ -12,6 +12,10 @@ module Diesel
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def perform
|
15
|
+
if endpoint.url.base_host
|
16
|
+
endpoint.url.subdomain = options[:subdomain]
|
17
|
+
end
|
18
|
+
|
15
19
|
env = {
|
16
20
|
method: endpoint.request_method,
|
17
21
|
url: endpoint.url,
|
@@ -20,6 +24,7 @@ module Diesel
|
|
20
24
|
logger: logger,
|
21
25
|
context: self
|
22
26
|
}
|
27
|
+
|
23
28
|
endpoint.middleware_stack.call(env)
|
24
29
|
perform_request(env)
|
25
30
|
end
|
@@ -46,7 +51,8 @@ module Diesel
|
|
46
51
|
|
47
52
|
protected
|
48
53
|
def perform_request(env)
|
49
|
-
HTTParty.send(env[:method],
|
54
|
+
HTTParty.send(env[:method],
|
55
|
+
env[:url].to_s,
|
50
56
|
headers: env[:request_headers],
|
51
57
|
query: env[:params],
|
52
58
|
body: env[:body])
|
@@ -18,32 +18,48 @@ module Diesel
|
|
18
18
|
specification.external_docs = build_node(ExternalDocs, json['externalDocs'])
|
19
19
|
specification.schemes = json['schemes']
|
20
20
|
specification.produces = json['produces'] || []
|
21
|
-
specification.
|
22
|
-
specification
|
23
|
-
|
24
|
-
security.scopes = v
|
25
|
-
memo
|
26
|
-
end
|
21
|
+
specification.consumes = json['consumes'] || []
|
22
|
+
build_security_definition_hash(specification, json)
|
23
|
+
build_security_hash(specification, json)
|
27
24
|
specification.paths = build_node_hash(Path, json, 'paths') do |path, path_json|
|
28
25
|
[:get, :put, :post, :delete, :options, :head, :patch].each do |method|
|
29
26
|
if op_json = path_json[method.to_s]
|
30
27
|
op = build_node(Operation, op_json, constructor_args: [method])
|
31
28
|
op.external_docs = build_node(ExternalDocs, op_json['externalDocs'])
|
32
|
-
op.parameters = build_node_list(Parameter, op_json, 'parameters')
|
29
|
+
op.parameters = build_node_list(Parameter, op_json, 'parameters') do |param, param_json|
|
30
|
+
if param_json["schema"] && param_json["schema"].kind_of?(Hash)
|
31
|
+
schema_json = param_json["schema"]
|
32
|
+
param.schema = build_node(Definition, schema_json)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
build_security_definition_hash(specification, json)
|
36
|
+
build_security_hash(op, op_json)
|
33
37
|
path.send("#{method}=".to_sym, op)
|
34
38
|
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
specification.definitions = build_node_hash(Definition, json, 'definitions') do |definition, def_json|
|
38
42
|
definition.properties = build_node_hash(Property, def_json, 'properties') do |prop, prop_json|
|
39
|
-
prop.enum = prop_json[
|
40
|
-
prop.items = prop_json[
|
43
|
+
prop.enum = prop_json["enum"]
|
44
|
+
prop.items = prop_json["items"]
|
41
45
|
end
|
42
46
|
end
|
43
47
|
specification
|
44
48
|
end
|
45
49
|
|
46
50
|
protected
|
51
|
+
def build_security_hash(parent_node, json)
|
52
|
+
sec_json = json['security']
|
53
|
+
parent_node.security = (sec_json || {}).reduce({}) do |memo, (k,v)|
|
54
|
+
memo[k] = security = Security.new(k)
|
55
|
+
security.scopes = v
|
56
|
+
memo
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_security_definition_hash(parent_node, json)
|
61
|
+
parent_node.security_definitions = build_node_hash(SecurityDefinition, json, 'securityDefinitions')
|
62
|
+
end
|
47
63
|
|
48
64
|
def build_node(model_class, json, options = {})
|
49
65
|
if json
|
data/lib/diesel/uri.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Diesel
|
2
|
+
class URI
|
3
|
+
|
4
|
+
components = [:scheme, :host, :base_host, :subdomain, :base_path, :resource_path]
|
5
|
+
|
6
|
+
attr_reader *components
|
7
|
+
|
8
|
+
components.each do |comp|
|
9
|
+
define_method "#{comp}=".to_sym do |v|
|
10
|
+
instance_variable_set("@#{comp}", v)
|
11
|
+
build_url
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
build_url
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def build_url
|
22
|
+
host = if base_host && subdomain
|
23
|
+
[subdomain, base_host].join('.')
|
24
|
+
else
|
25
|
+
self.host
|
26
|
+
end
|
27
|
+
[
|
28
|
+
scheme,
|
29
|
+
'://',
|
30
|
+
host,
|
31
|
+
base_path,
|
32
|
+
resource_path
|
33
|
+
].join('')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|