Skip to content

Commit 76cc18a

Browse files
authored
fix: Ensure CORS headers are still added when an error occurs in the authenticator (#32)
* fix: Ensure CORS headers are still added when an error occurs in the authenticator * fix: Fix failing tests in Rack >= 3.1.0 The `rack.input` header was made optional, so we need to always set an input in our mock requests * chore: Fix linting issues - add_runtime_dependency is deprecated in favour of add_dependency - params are not required for `super` when the method signature is unchanged * test: Add status check
1 parent 0907fcf commit 76cc18a

File tree

7 files changed

+46
-10
lines changed

7 files changed

+46
-10
lines changed

apia.gemspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ Gem::Specification.new do |s|
1313
s.authors = ['Adam Cooke']
1414
s.email = ['adam@k.io']
1515
s.licenses = ['MIT']
16-
s.add_runtime_dependency 'json'
17-
s.add_runtime_dependency 'rack'
16+
s.add_dependency 'json'
17+
s.add_dependency 'rack'
1818
end

lib/apia/dsls/endpoint.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def field(name, *args, type: nil, **options, &block)
6969

7070
field :pagination, type: PaginationObject
7171
end
72-
super(name, *args, type: type, **options, &block)
72+
super
7373
end
7474

7575
def fields(fieldset)

lib/apia/endpoint.rb

+7-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def execute(request)
4747
response = Response.new(request, self)
4848
environment = RequestEnvironment.new(request, response)
4949

50-
catch_errors(response) do
50+
catch_errors(response, environment) do
5151
# Determine an authenticator for this endpoint
5252
request.authenticator = definition.authenticator || request.controller&.definition&.authenticator || request.api&.definition&.authenticator
5353

@@ -92,10 +92,14 @@ def execute(request)
9292
#
9393
# @param response [Apia::Response]
9494
# @return [void]
95-
def catch_errors(response)
95+
def catch_errors(response, environment)
9696
yield
9797
rescue Apia::RuntimeError => e
98-
catch_errors(response) do
98+
# If the error was triggered by the authenticator, the cors headers wont yet have been merged
99+
# so ensure cors headers are merged here
100+
response.headers.merge!(environment.cors.to_headers)
101+
102+
catch_errors(response, environment) do
99103
response.body = { error: e.hash }
100104
response.status = e.http_status
101105
response.headers['x-api-schema'] = 'json-error'

lib/apia/request.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def json_body
3636
end
3737

3838
def body?
39-
has_header?('rack.input')
39+
has_header?(::Rack::RACK_INPUT)
4040
end
4141

4242
def params

spec/specs/apia/argument_set_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
end
4141

4242
it 'should create a new set using HTTP params if provided' do
43-
env = Rack::MockRequest.env_for('/?name=michael')
43+
env = Rack::MockRequest.env_for('/?name=michael', input: '')
4444
request = Apia::Request.new(env)
4545
as = Apia::ArgumentSet.create('ExampleSet') do
4646
argument :name, type: :string

spec/specs/apia/endpoint_spec.rb

+33-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
end
131131

132132
it 'should create an argument set from standard HTTP query string parameters' do
133-
request = Apia::Request.new(Rack::MockRequest.env_for('/?name=Adam'))
133+
request = Apia::Request.new(Rack::MockRequest.env_for('/?name=Adam', input: ''))
134134
request.endpoint = Apia::Endpoint.create('Endpoint') do
135135
argument :name, type: :string
136136
end
@@ -173,6 +173,38 @@
173173
expect(response.headers['Access-Control-Allow-Headers']).to eq 'X-Custom'
174174
end
175175
end
176+
177+
context 'when cors values are set by the authenticator and it throws an error' do
178+
it 'includes the CORS headers from the authenticator in the response' do
179+
request = Apia::Request.new(Rack::MockRequest.env_for('/', input: ''))
180+
181+
authenticator = Apia::Authenticator.create('ExampleAPIAuthenticator') do
182+
potential_error 'Failed' do
183+
code :failed
184+
http_status 500
185+
end
186+
end
187+
188+
authenticator.action do
189+
cors.origin = 'example.com'
190+
cors.methods = 'GET, POST'
191+
cors.headers = 'X-Custom'
192+
193+
raise_error 'Failed'
194+
end
195+
196+
endpoint = Apia::Endpoint.create('Endpoint') do
197+
authenticator authenticator
198+
end
199+
200+
response = endpoint.execute(request)
201+
202+
expect(response.status).to eq 500
203+
expect(response.headers['Access-Control-Allow-Origin']).to eq 'example.com'
204+
expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, POST'
205+
expect(response.headers['Access-Control-Allow-Headers']).to eq 'X-Custom'
206+
end
207+
end
176208
end
177209

178210
context 'when the request is an OPTIONS request' do

spec/specs/apia/request_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
end
4242

4343
it 'should return a hash if no body is provided but there is an _arguments parameter containing a string' do
44-
request = Apia::Request.new(Rack::MockRequest.env_for('/', params: { _arguments: '{"name":"Jamie"}' }))
44+
request = Apia::Request.new(Rack::MockRequest.env_for('/', params: { _arguments: '{"name":"Jamie"}' }, input: ''))
4545
expect(request.json_body).to be_a Hash
4646
expect(request.json_body['name']).to eq 'Jamie'
4747
end

0 commit comments

Comments
 (0)