drillbit 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/drillbit.rb +3 -1
- data/lib/drillbit/authorizable_resource.rb +6 -0
- data/lib/drillbit/authorizers/parameters/filtering.rb +1 -0
- data/lib/drillbit/authorizers/parameters/resource.rb +59 -0
- data/lib/drillbit/errors/invalid_request_body.rb +29 -0
- data/lib/drillbit/middleware/api_request_validator.rb +40 -0
- data/lib/drillbit/middleware/parameter_parser.rb +61 -0
- data/lib/drillbit/middleware/token_processor.rb +26 -0
- data/lib/drillbit/requests/base.rb +8 -5
- data/lib/drillbit/responses/invalid_request_body.rb +18 -0
- data/lib/drillbit/tokens/json_web_token.rb +9 -1
- data/lib/drillbit/version.rb +1 -1
- data/spec/drillbit/accept_header_spec.rb +2 -2
- data/spec/drillbit/authorizers/parameters/filtering_spec.rb +4 -4
- data/spec/drillbit/authorizers/parameters/resource_spec.rb +4 -4
- data/spec/drillbit/authorizers/parameters_spec.rb +3 -3
- data/spec/drillbit/authorizers/query_spec.rb +3 -3
- data/spec/drillbit/authorizers/scope_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_api_request_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_request_body_spec.rb +25 -0
- data/spec/drillbit/errors/invalid_subdomain_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_token_spec.rb +3 -3
- data/spec/drillbit/invalid_subdomain_spec.rb +3 -3
- data/spec/drillbit/invalid_token_spec.rb +3 -3
- data/spec/drillbit/matchers/accept_header_spec.rb +3 -3
- data/spec/drillbit/matchers/subdomain_spec.rb +3 -3
- data/spec/drillbit/matchers/version_spec.rb +3 -3
- data/spec/drillbit/middleware/{api_request_spec.rb → api_request_validator_spec.rb} +11 -46
- data/spec/drillbit/middleware/parameter_parser_spec.rb +184 -0
- data/spec/drillbit/middleware/token_processor_spec.rb +27 -0
- data/spec/drillbit/requests/base_spec.rb +3 -3
- data/spec/drillbit/requests/rack_spec.rb +3 -3
- data/spec/drillbit/requests/rails_spec.rb +3 -3
- data/spec/drillbit/resource/model_spec.rb +3 -3
- data/spec/drillbit/resource/processors/filtering_spec.rb +4 -4
- data/spec/drillbit/resource/processors/indexing_spec.rb +4 -4
- data/spec/drillbit/resource/processors/paging_spec.rb +4 -4
- data/spec/drillbit/resource/processors/sorting_spec.rb +4 -4
- data/spec/drillbit/tokens/base64_spec.rb +3 -3
- data/spec/drillbit/tokens/json_web_token_spec.rb +11 -3
- data/spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb +4 -4
- metadata +15 -8
- metadata.gz.sig +0 -0
- data/lib/drillbit/middleware/api_request.rb +0 -49
- data/lib/drillbit/parameters.rb +0 -22
- data/spec/drillbit/parameters_spec.rb +0 -49
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/errors/invalid_request_body'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidRequestBody do
|
8
|
+
let(:error) { InvalidRequestBody.new }
|
9
|
+
|
10
|
+
it 'has a status of 400' do
|
11
|
+
expect(error.http_status).to eql 400
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has a code' do
|
15
|
+
expect(error.code).to eql 'errors.invalid_request_body'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can output the detail' do
|
19
|
+
expect(error.detail).to eql \
|
20
|
+
'The information you attempted to send in the request cannot be parsed as ' \
|
21
|
+
'a valid JSON document.'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/errors/invalid_subdomain'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidSubdomain do
|
8
8
|
let(:error) { InvalidSubdomain.new }
|
9
9
|
|
10
10
|
it 'has a status of 404' do
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/errors/invalid_token'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidToken do
|
8
8
|
let(:error) { InvalidToken.new }
|
9
9
|
|
10
10
|
it 'has a status of 401' do
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/responses/invalid_subdomain'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Responses
|
7
|
+
describe InvalidSubdomain, singletons: Erratum::Configuration do
|
8
8
|
it 'returns the proper response' do
|
9
9
|
Erratum.configuration.url_mappings = {
|
10
10
|
'external_documentation_urls' => {
|
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
require 'drillbit/responses/invalid_token'
|
4
4
|
|
5
5
|
# rubocop:disable Metrics/LineLength
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Responses
|
8
|
+
describe InvalidToken, singletons: Erratum::Configuration do
|
9
9
|
it 'returns the proper response' do
|
10
10
|
Erratum.configuration.url_mappings = {
|
11
11
|
'external_documentation_urls' => {
|
@@ -4,9 +4,9 @@ require 'drillbit/requests/base'
|
|
4
4
|
require 'drillbit/matchers/accept_header'
|
5
5
|
|
6
6
|
# rubocop:disable Metrics/LineLength
|
7
|
-
module
|
8
|
-
module
|
9
|
-
|
7
|
+
module Drillbit
|
8
|
+
module Matchers
|
9
|
+
describe AcceptHeader do
|
10
10
|
it 'matches if the subdomain is API and the accept header is valid' do
|
11
11
|
env = {
|
12
12
|
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
@@ -4,9 +4,9 @@ require 'drillbit/requests/base'
|
|
4
4
|
require 'drillbit/matchers/subdomain'
|
5
5
|
require 'drillbit/configuration'
|
6
6
|
|
7
|
-
module
|
8
|
-
module
|
9
|
-
|
7
|
+
module Drillbit
|
8
|
+
module Matchers
|
9
|
+
describe Subdomain do
|
10
10
|
before(:each) do
|
11
11
|
Drillbit.configuration.allowed_subdomains = %w{api}
|
12
12
|
Drillbit.configuration.allowed_api_subdomains = %w{api}
|
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
require 'drillbit/requests/base'
|
4
4
|
require 'drillbit/matchers/version'
|
5
5
|
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Matchers
|
8
|
+
describe Version do
|
9
9
|
context 'when the version is passed in the accept header' do
|
10
10
|
it 'does not match if the subdomain is API but the requested version does not ' \
|
11
11
|
'equal the version constraint' do
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'spec_helper'
|
3
|
-
require 'drillbit/middleware/
|
3
|
+
require 'drillbit/middleware/api_request_validator'
|
4
4
|
|
5
5
|
# rubocop:disable Metrics/LineLength
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Middleware
|
8
|
+
describe ApiRequestValidator, singletons: Erratum::Configuration do
|
9
9
|
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
10
10
|
|
11
11
|
before(:each) do
|
@@ -26,7 +26,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'allows requests for allowed subdomains without accept headers' do
|
29
|
-
api_request_middleware =
|
29
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
30
30
|
|
31
31
|
request = {
|
32
32
|
'HTTP_HOST' => 'westeros.example.com',
|
@@ -42,7 +42,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'does not allow requests if they are not for an allowed subdomain' do
|
45
|
-
api_request_middleware =
|
45
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
46
46
|
|
47
47
|
request = {
|
48
48
|
'HTTP_HOST' => 'notvalid.example.com',
|
@@ -78,7 +78,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
78
78
|
it 'does not allow requests if they are for an allowed subdomain but does ' \
|
79
79
|
'not have a valid accept header' do
|
80
80
|
|
81
|
-
api_request_middleware =
|
81
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
82
82
|
|
83
83
|
request = {
|
84
84
|
'HTTP_HOST' => 'api.example.com',
|
@@ -112,7 +112,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'does allow requests if both the subdomain and the accept header are valid' do
|
115
|
-
api_request_middleware =
|
115
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
116
116
|
|
117
117
|
request = {
|
118
118
|
'HTTP_HOST' => 'api.example.com',
|
@@ -129,7 +129,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
129
129
|
|
130
130
|
it 'does allow requests if the subdomain, the accept header and the token are valid' do
|
131
131
|
Drillbit.configuration.token_private_key = test_private_key
|
132
|
-
api_request_middleware =
|
132
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
133
133
|
|
134
134
|
request = {
|
135
135
|
'HTTP_HOST' => 'api.example.com',
|
@@ -145,43 +145,8 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
145
145
|
expect(response).to eql 'response'
|
146
146
|
end
|
147
147
|
|
148
|
-
it 'returns the proper response if the token is invalid' do
|
149
|
-
Drillbit.configuration.token_private_key = test_private_key
|
150
|
-
api_request_middleware = ApiRequest.new(app)
|
151
|
-
|
152
|
-
request = {
|
153
|
-
'HTTP_HOST' => 'api.example.com',
|
154
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
155
|
-
'HTTP_AUTHORIZATION' => "Token #{invalid_jwe_token}",
|
156
|
-
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
157
|
-
}
|
158
|
-
|
159
|
-
_status, _headers, response = api_request_middleware.call(request)
|
160
|
-
|
161
|
-
expect(response.first).to include 'errors.invalid_token'
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'converts JSON API compliant dasherized query params to underscored' do
|
165
|
-
app = ->(env) { [200, env, 'response'] }
|
166
|
-
api_request_middleware = ApiRequest.new(app)
|
167
|
-
|
168
|
-
request = {
|
169
|
-
'HTTP_HOST' => 'api.example.com',
|
170
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
171
|
-
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
172
|
-
'nice-to-meet=you-bob&' \
|
173
|
-
'hows-the-weather=today-bob',
|
174
|
-
}
|
175
|
-
|
176
|
-
_status, headers, _response = api_request_middleware.call(request)
|
177
|
-
|
178
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
179
|
-
'nice_to_meet=you-bob&' \
|
180
|
-
'hows_the_weather=today-bob'
|
181
|
-
end
|
182
|
-
|
183
148
|
it 'properly converts the content type for Rails when it is the only one' do
|
184
|
-
api_request_middleware =
|
149
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
185
150
|
|
186
151
|
request = {
|
187
152
|
'CONTENT_TYPE' => 'application/vnd.api+json',
|
@@ -199,7 +164,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
199
164
|
end
|
200
165
|
|
201
166
|
it 'properly converts the content type for Rails when it is not the only one' do
|
202
|
-
api_request_middleware =
|
167
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
203
168
|
|
204
169
|
request = {
|
205
170
|
'CONTENT_TYPE' => 'application/vnd.api+json;other',
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/middleware/parameter_parser'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Middleware
|
7
|
+
describe ParameterParser do
|
8
|
+
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
9
|
+
|
10
|
+
it 'converts JSON API compliant dasherized query params to underscored' do
|
11
|
+
app = ->(env) { [200, env, 'response'] }
|
12
|
+
middleware = ParameterParser.new(app)
|
13
|
+
|
14
|
+
request = {
|
15
|
+
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
16
|
+
'nice-to-meet=you-bob&' \
|
17
|
+
'hows-the-weather=today-bob',
|
18
|
+
}
|
19
|
+
|
20
|
+
_status, headers, _response = middleware.call(request)
|
21
|
+
|
22
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
23
|
+
'nice_to_meet=you-bob&' \
|
24
|
+
'hows_the_weather=today-bob'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not touch query params with no dashes' do
|
28
|
+
app = ->(env) { [200, env, 'response'] }
|
29
|
+
middleware = ParameterParser.new(app)
|
30
|
+
|
31
|
+
request = {
|
32
|
+
'QUERY_STRING' => 'hello_there=bob-jones',
|
33
|
+
}
|
34
|
+
|
35
|
+
_status, headers, _response = middleware.call(request)
|
36
|
+
|
37
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'can handle weirdly formatted query string parameters' do
|
41
|
+
app = ->(env) { [200, env, 'response'] }
|
42
|
+
middleware = ParameterParser.new(app)
|
43
|
+
|
44
|
+
request = {
|
45
|
+
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
46
|
+
'nice-to-meet=you-bob&=',
|
47
|
+
}
|
48
|
+
|
49
|
+
_status, headers, _response = middleware.call(request)
|
50
|
+
|
51
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
52
|
+
'nice_to_meet=you-bob&='
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'can handle query string parameters with no values' do
|
56
|
+
app = ->(env) { [200, env, 'response'] }
|
57
|
+
middleware = ParameterParser.new(app)
|
58
|
+
|
59
|
+
request = {
|
60
|
+
'QUERY_STRING' => 'hello-there&nice-to-meet=you-bob&=',
|
61
|
+
}
|
62
|
+
|
63
|
+
_status, headers, _response = middleware.call(request)
|
64
|
+
|
65
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there&nice_to_meet=you-bob&='
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'can handle query string parameters with parameter name' do
|
69
|
+
app = ->(env) { [200, env, 'response'] }
|
70
|
+
middleware = ParameterParser.new(app)
|
71
|
+
|
72
|
+
request = {
|
73
|
+
'QUERY_STRING' => 'hello-there=bob-jones&=you-bob&nice-to-meet=you-bob&=',
|
74
|
+
}
|
75
|
+
|
76
|
+
_status, headers, _response = middleware.call(request)
|
77
|
+
|
78
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
79
|
+
'=you-bob&' \
|
80
|
+
'nice_to_meet=you-bob&='
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'converts JSON API compliant dasherized request params to underscored' do
|
84
|
+
app = ->(env) { [200, env, 'response'] }
|
85
|
+
middleware = ParameterParser.new(app)
|
86
|
+
|
87
|
+
request = {
|
88
|
+
'CONTENT_LENGTH' => '1',
|
89
|
+
'CONTENT_TYPE' => 'application/json',
|
90
|
+
'RACK_INPUT' => <<-HEREDOC
|
91
|
+
{
|
92
|
+
"single": "word",
|
93
|
+
"double-word": "double double",
|
94
|
+
"triple": {
|
95
|
+
"trippple-tripple": "mcdipple",
|
96
|
+
"quad": {
|
97
|
+
"another-level": "whoa inception"
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
HEREDOC
|
102
|
+
}
|
103
|
+
|
104
|
+
_status, headers, _response = middleware.call(request)
|
105
|
+
|
106
|
+
expect(JSON.load(headers['RACK_INPUT'])).to eql(
|
107
|
+
'single' => 'word',
|
108
|
+
'double_word' => 'double double',
|
109
|
+
'triple' => {
|
110
|
+
'trippple_tripple' => 'mcdipple',
|
111
|
+
'quad' => {
|
112
|
+
'another_level' => 'whoa inception',
|
113
|
+
},
|
114
|
+
},
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not convert to JSON if the content type is not JSON' do
|
119
|
+
app = ->(env) { [200, env, 'response'] }
|
120
|
+
middleware = ParameterParser.new(app)
|
121
|
+
|
122
|
+
request = {
|
123
|
+
'CONTENT_LENGTH' => '1',
|
124
|
+
'CONTENT_TYPE' => 'not_j_s_o_n',
|
125
|
+
'RACK_INPUT' => 'This is not JSON',
|
126
|
+
}
|
127
|
+
|
128
|
+
_status, headers, _response = middleware.call(request)
|
129
|
+
|
130
|
+
expect(headers['RACK_INPUT']).to eql 'This is not JSON'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'does not convert to JSON if there is no content' do
|
134
|
+
app = ->(env) { [200, env, 'response'] }
|
135
|
+
middleware = ParameterParser.new(app)
|
136
|
+
|
137
|
+
request = {
|
138
|
+
'CONTENT_LENGTH' => '0',
|
139
|
+
'CONTENT_TYPE' => 'application/json',
|
140
|
+
'RACK_INPUT' => 'empty',
|
141
|
+
}
|
142
|
+
|
143
|
+
_status, headers, _response = middleware.call(request)
|
144
|
+
|
145
|
+
expect(headers['RACK_INPUT']).to eql 'empty'
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'can handle incorrectly formatted JSON' do
|
149
|
+
app = ->(env) { [200, env, 'response'] }
|
150
|
+
middleware = ParameterParser.new(app)
|
151
|
+
|
152
|
+
request = {
|
153
|
+
'CONTENT_LENGTH' => '1',
|
154
|
+
'CONTENT_TYPE' => 'application/json',
|
155
|
+
'RACK_INPUT' => 'something blah',
|
156
|
+
}
|
157
|
+
|
158
|
+
status, headers, response = middleware.call(request)
|
159
|
+
|
160
|
+
expect(status).to eql 400
|
161
|
+
expect(headers).to eql({})
|
162
|
+
expect(JSON.load(response[0])).to include(
|
163
|
+
'errors' => [
|
164
|
+
{
|
165
|
+
'id' => match(/[a-z0-9\-]+/),
|
166
|
+
'links' => {
|
167
|
+
'about' => nil,
|
168
|
+
'documentation' => nil,
|
169
|
+
},
|
170
|
+
'status' => 400,
|
171
|
+
'code' => 'errors.invalid_request_body',
|
172
|
+
'title' => 'Invalid Request Body',
|
173
|
+
'detail' => 'The information you attempted to send in the ' \
|
174
|
+
'request cannot be parsed as a valid JSON document.',
|
175
|
+
'source' => {
|
176
|
+
'request_body' => 'something blah',
|
177
|
+
},
|
178
|
+
},
|
179
|
+
],
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/middleware/token_processor'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Middleware
|
7
|
+
describe TokenProcessor do
|
8
|
+
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
9
|
+
|
10
|
+
it 'returns the proper response if the token is invalid' do
|
11
|
+
Drillbit.configuration.token_private_key = test_private_key
|
12
|
+
middleware = TokenProcessor.new(app)
|
13
|
+
|
14
|
+
request = {
|
15
|
+
'HTTP_HOST' => 'api.example.com',
|
16
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
17
|
+
'HTTP_AUTHORIZATION' => "Token #{invalid_jwe_token}",
|
18
|
+
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
19
|
+
}
|
20
|
+
|
21
|
+
_status, _headers, response = middleware.call(request)
|
22
|
+
|
23
|
+
expect(response.first).to include 'errors.invalid_token'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -3,9 +3,9 @@ require 'ostruct'
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'drillbit/requests/base'
|
5
5
|
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Requests
|
8
|
+
describe Base do
|
9
9
|
it 'can resolve itself by returning itself' do
|
10
10
|
raw_request = Base.new(token_private_key: '', request: {})
|
11
11
|
resolved_request = Base.resolve(raw_request)
|