apill 1.6.0 → 2.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6e916c78acf4e6b77bba21330da5ec51c64dc14
4
- data.tar.gz: 5a52f7ab6a1ab045168db125dccf391fbde97e5b
3
+ metadata.gz: 974dc18ca764506d1a544244dfb65b70ce344ec7
4
+ data.tar.gz: a74c5b8c216e451a7d25015e623363b2b0348b2e
5
5
  SHA512:
6
- metadata.gz: bbf3a7730599ec3d42223988e7e9732532dd72bde970aff017345c1cb4a3b959c7b7ae441f6b14795cac269fe944d9fbdfd92c2567e2f81ae76383bb523406dd
7
- data.tar.gz: 882474601d4956b23b8aa815cc2ca91318639de79d26614e94694902db5c119332dfdfafa7b6ddf48ead4dab0a87a657320f0b4fca8d5766a720b39e92a7c07c
6
+ metadata.gz: 9e248544358f6cf0a0559504b360ca48ba560fdb88a59b23e6007acfddbe67c1c30e7e5de9abcfa3a2191c8d55065af654f4f685151d7c1cce61edc540e7afee
7
+ data.tar.gz: 0cd92dc06458725f440594001bce76f56a00625f88ee2d9afe56981972ac54c4d838ed50eaa1bdbfdbe8f008646bcf02c3614fe2bbe0d2fd4e8f6b99c898eb7b
@@ -0,0 +1,24 @@
1
+ module Apill
2
+ class Configuration
3
+ attr_accessor \
4
+ :allowed_subdomains,
5
+ :application_name,
6
+ :default_api_version
7
+
8
+ def to_h
9
+ {
10
+ allowed_subdomains: allowed_subdomains,
11
+ application_name: application_name,
12
+ default_api_version: api_version,
13
+ }
14
+ end
15
+ end
16
+
17
+ def self.configure
18
+ yield configuration
19
+ end
20
+
21
+ def self.configuration
22
+ @configuration ||= Configuration.new
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require 'human_error'
2
+
3
+ module Apill
4
+ module Errors
5
+ class InvalidSubdomainError < HumanError::Errors::RequestError
6
+ attr_accessor :http_host
7
+
8
+ def http_status
9
+ 404
10
+ end
11
+
12
+ def developer_message
13
+ 'The resource you attempted to access is either not authorized for the ' \
14
+ 'authenticated user or does not exist.'
15
+ end
16
+
17
+ def developer_details
18
+ { http_host: http_host }
19
+ end
20
+
21
+ def friendly_message
22
+ 'Sorry! The resource you tried to access does not exist.'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,7 +8,6 @@ class AcceptHeaderMatcher
8
8
  def matches?(request)
9
9
  super
10
10
 
11
- request.subdomains.first == 'api' &&
12
11
  accept_header.valid?
13
12
  end
14
13
  end
@@ -13,28 +13,31 @@ module GenericMatcher
13
13
  end
14
14
 
15
15
  def matches?(request)
16
- self.accept_header = get_accept_header(raw_header_from_headers: request.headers['Accept'],
17
- raw_header_from_params: request.params['accept'])
16
+ self.application = request['API_APPLICATION_NAME']
17
+ self.accept_header = get_accept_header(raw_header_from_headers: request['HTTP_ACCEPT'],
18
+ raw_header_from_params: request['QUERY_STRING'])
18
19
  end
19
20
 
20
21
  private
21
22
 
22
23
  def get_accept_header(raw_header_from_headers:, raw_header_from_params:)
23
- header_from_header = accept_header_from_header(raw_header_from_headers)
24
+ header_from_header = accept_header_from_string(raw_header_from_headers)
24
25
 
25
- return header_from_header if header_from_header.valid? || raw_header_from_params.nil?
26
+ return header_from_header if header_from_header.valid? ||
27
+ raw_header_from_params.to_s.empty?
26
28
 
27
- accept_header_from_header(raw_header_from_params)
29
+ accept_header_from_params(raw_header_from_params)
28
30
  end
29
31
 
30
- def accept_header_from_header(raw_header_from_headers='')
32
+ def accept_header_from_string(raw_header_from_headers='')
31
33
  Apill::AcceptHeader.new(application: application,
32
34
  header: raw_header_from_headers)
33
35
  end
34
36
 
35
37
  def accept_header_from_params(raw_header_from_params='')
36
- Apill::AcceptHeader.new(application: application,
37
- header: raw_header_from_params)
38
+ header_from_params = raw_header_from_params[%r{(?:\A|&)accept=(.+?)(?=\z|&)}, 1]
39
+
40
+ accept_header_from_string(header_from_params)
38
41
  end
39
42
  end
40
43
  end
@@ -1,17 +1,19 @@
1
1
  module Apill
2
2
  module Matchers
3
3
  class SubdomainMatcher
4
- def initialize(subdomain: 'api')
5
- self.subdomain = subdomain
4
+ def initialize(allowed_subdomains: ['api'])
5
+ self.allowed_subdomains = Array(allowed_subdomains)
6
6
  end
7
7
 
8
8
  def matches?(request)
9
- request.subdomains.first == subdomain
9
+ request_subdomain = request['HTTP_HOST'][/\A([a-z\-]+)/i, 1]
10
+
11
+ allowed_subdomains.include? request_subdomain
10
12
  end
11
13
 
12
14
  protected
13
15
 
14
- attr_accessor :subdomain
16
+ attr_accessor :allowed_subdomains
15
17
  end
16
18
  end
17
19
  end
@@ -1,5 +1,4 @@
1
1
  require 'apill/matchers/generic_matcher'
2
- require 'apill/errors/invalid_api_request_error'
3
2
 
4
3
  module Apill
5
4
  module Matchers
@@ -12,10 +11,7 @@ class VersionMatcher
12
11
  def matches?(request)
13
12
  super
14
13
 
15
- raise Apill::Errors::InvalidApiRequestError unless accept_header.valid?
16
-
17
- request.subdomains.first == 'api' &&
18
- requested_version == version_constraint
14
+ requested_version == version_constraint
19
15
  end
20
16
 
21
17
  private
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+ require 'apill/configuration'
3
+ require 'apill/matchers/subdomain_matcher'
4
+ require 'apill/matchers/invalid_api_request_matcher'
5
+ require 'apill/responses/invalid_api_request_response'
6
+ require 'apill/responses/invalid_subdomain_response'
7
+
8
+ module Apill
9
+ module Middleware
10
+ class ApiRequest
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ env['API_APPLICATION_NAME'] = Apill.configuration.application_name
17
+
18
+ if Matchers::SubdomainMatcher.new(allowed_subdomains: Apill.configuration.allowed_subdomains).
19
+ matches?(env)
20
+
21
+ if Matchers::AcceptHeaderMatcher.new.matches?(env)
22
+ @app.call(env)
23
+ else
24
+ Responses::InvalidApiRequestResponse.call(env)
25
+ end
26
+ else
27
+ Responses::InvalidSubdomainResponse.call(env)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ require 'apill/errors/invalid_subdomain_error'
2
+
3
+ module Apill
4
+ module Responses
5
+ class InvalidSubdomainResponse
6
+ def self.call(env)
7
+ error = Apill::Errors::InvalidSubdomainError.new(http_host: env['HTTP_HOST'])
8
+
9
+ [
10
+ error.http_status, # HTTP Status Code
11
+ {}, # Response Headers
12
+ [ error.to_json ], # Message
13
+ ]
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/apill/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Apill
2
- VERSION = '1.6.0'
2
+ VERSION = '2.0.0'
3
3
  end
File without changes
@@ -0,0 +1,39 @@
1
+ require 'rspectacular'
2
+ require 'apill/errors/invalid_subdomain_error'
3
+
4
+ module Apill
5
+ module Errors
6
+ describe InvalidSubdomainError do
7
+ let(:error) { InvalidSubdomainError.new }
8
+
9
+ it 'has a status of 404' do
10
+ expect(error.http_status).to eql 404
11
+ end
12
+
13
+ it 'has a code of 1010' do
14
+ expect(error.code).to eql 1010
15
+ end
16
+
17
+ it 'has a knowledgebase article ID of 1234567890' do
18
+ expect(error.knowledgebase_article_id).to eql '1234567890'
19
+ end
20
+
21
+ it 'can output the developer message' do
22
+ expect(error.developer_message).to eql \
23
+ 'The resource you attempted to access is either not authorized for the ' \
24
+ 'authenticated user or does not exist.'
25
+ end
26
+
27
+ it 'can output the developer details' do
28
+ error = InvalidSubdomainError.new http_host: 'foo'
29
+
30
+ expect(error.developer_details).to eql(http_host: 'foo')
31
+ end
32
+
33
+ it 'can output the friendly message' do
34
+ expect(error.friendly_message).to eql \
35
+ 'Sorry! The resource you tried to access does not exist.'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'rspectacular'
2
+ require 'apill/responses/invalid_subdomain_response'
3
+
4
+ module Apill
5
+ module Responses
6
+ describe InvalidSubdomainResponse do
7
+ before(:each) do
8
+ HumanError.configuration.api_error_documentation_url = 'http://error.com'
9
+ HumanError.configuration.knowledgebase_url = 'http://knowledge.com'
10
+ HumanError.configuration.api_version = '1'
11
+ end
12
+
13
+ it 'returns the proper response' do
14
+ request = { 'HTTP_HOST' => 'api.example.com' }
15
+ response = InvalidSubdomainResponse.call(request)
16
+
17
+ expect(response).to eql(
18
+ [
19
+ 404,
20
+ {},
21
+ [
22
+ '{' \
23
+ '"error":' \
24
+ '{' \
25
+ '"status":404,' \
26
+ '"code":1010,' \
27
+ '"developer_documentation_uri":"http://error.com/1010?version=1",' \
28
+ '"customer_support_uri":"http://knowledge.com/1234567890",' \
29
+ '"developer_message_key":"errors.invalid.subdomain.error.developer",' \
30
+ '"developer_message":"The resource you attempted to access is either not authorized for the authenticated user or does not exist.",' \
31
+ '"developer_details":' \
32
+ '{' \
33
+ '"http_host":"api.example.com"' \
34
+ '},' \
35
+ '"friendly_message_key":"errors.invalid.subdomain.error.friendly",' \
36
+ '"friendly_message":"Sorry! The resource you tried to access does not exist."' \
37
+ '}' \
38
+ '}'
39
+ ]
40
+ ]
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,103 @@
1
+ require 'rspectacular'
2
+ require 'apill/matchers/accept_header_matcher'
3
+
4
+ module Apill
5
+ module Matchers
6
+ describe AcceptHeaderMatcher do
7
+ it 'matches if the subdomain is API and the accept header is valid' do
8
+ request = {
9
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion;version=1.0.0',
10
+ 'API_APPLICATION_NAME' => 'matrix',
11
+ }
12
+
13
+ matcher = AcceptHeaderMatcher.new
14
+
15
+ expect(matcher.matches?(request)).to be_a TrueClass
16
+ end
17
+
18
+ it 'matches if the subdomain is API and the accept header is passed in as ' \
19
+ 'a parameter' do
20
+
21
+ request = {
22
+ 'QUERY_STRING' => 'accept=application/vnd.matrix+zion;version=1.0.0',
23
+ 'API_APPLICATION_NAME' => 'matrix',
24
+ }
25
+
26
+ matcher = AcceptHeaderMatcher.new
27
+
28
+ expect(matcher.matches?(request)).to be_a TrueClass
29
+ end
30
+
31
+ it 'matches if the subdomain is API and the accept header is passed in as a ' \
32
+ 'secondary parameter' do
33
+
34
+ request = {
35
+ 'QUERY_STRING' => 'first=my_param&accept=application/vnd.matrix+zion;version=1.0.0',
36
+ 'API_APPLICATION_NAME' => 'matrix',
37
+ }
38
+
39
+ matcher = AcceptHeaderMatcher.new
40
+
41
+ expect(matcher.matches?(request)).to be_a TrueClass
42
+ end
43
+
44
+ it 'matches the header accept header if the subdomain is API and the accept header ' \
45
+ 'is passed both as a valid header and as a parameter' do
46
+
47
+ request = {
48
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion;version=1.0.0',
49
+ 'QUERY_STRING' => 'accept=application/vnd.matrix+zion;version=2.0.0',
50
+ 'API_APPLICATION_NAME' => 'matrix',
51
+ }
52
+
53
+ matcher = AcceptHeaderMatcher.new
54
+ matcher.matches?(request)
55
+
56
+ expect(matcher.accept_header.version).to eql '1.0.0'
57
+ end
58
+
59
+ it 'matches the accept header parameter if the subdomain is API and the accept ' \
60
+ 'header is passed both as an invalid header as well as as a parameter' do
61
+
62
+ request = {
63
+ 'HTTP_ACCEPT' => 'application/vndmatrix+zion;version=1.0.0',
64
+ 'QUERY_STRING' => 'accept=application/vnd.matrix+zion;version=2.0.0',
65
+ 'API_APPLICATION_NAME' => 'matrix',
66
+ }
67
+
68
+ matcher = AcceptHeaderMatcher.new
69
+ matcher.matches?(request)
70
+
71
+ expect(matcher.accept_header.version).to eql '2.0.0'
72
+ end
73
+
74
+ it 'matches the accept header parameter if the subdomain is API and the accept ' \
75
+ 'header is passed both as an invalid header as well as as a parameter' do
76
+
77
+ request = {
78
+ 'HTTP_ACCEPT' => 'application/vndmatrix+zion;version=1.0.0',
79
+ 'QUERY_STRING' => '',
80
+ 'API_APPLICATION_NAME' => 'matrix',
81
+ }
82
+
83
+ matcher = AcceptHeaderMatcher.new
84
+ matcher.matches?(request)
85
+
86
+ expect(matcher.accept_header.raw_accept_header).to eql \
87
+ 'application/vndmatrix+zion;version=1.0.0'
88
+ end
89
+
90
+ it 'does not match if the subdomain is API but the accept header is invalid' do
91
+ request = {
92
+ 'HTTP_ACCEPT' => 'application/vndmatrix+zion;version=1.0.0',
93
+ 'QUERY_STRING' => '',
94
+ 'API_APPLICATION_NAME' => 'matrix',
95
+ }
96
+
97
+ matcher = AcceptHeaderMatcher.new
98
+
99
+ expect(matcher.matches?(request)).to be_a FalseClass
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,21 @@
1
+ require 'ostruct'
2
+ require 'rspectacular'
3
+ require 'apill/matchers/invalid_api_request_matcher'
4
+
5
+ module Apill
6
+ module Matchers
7
+ describe InvalidApiRequestMatcher do
8
+ it 'is the inverse of whether the accept header matches' do
9
+ request = {
10
+ 'HTTP_HOST' => 'api.example.com',
11
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion;version=1.0.0',
12
+ 'API_APPLICATION_NAME' => 'matrix',
13
+ }
14
+
15
+ matcher = InvalidApiRequestMatcher.new
16
+
17
+ expect(matcher.matches?(request)).to be_a FalseClass
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ require 'rspectacular'
2
+ require 'apill/matchers/subdomain_matcher'
3
+
4
+ module Apill
5
+ module Matchers
6
+ describe SubdomainMatcher do
7
+ it 'matches if the subdomain is API' do
8
+ matcher = SubdomainMatcher.new
9
+ request = { 'HTTP_HOST' => 'api.example.com' }
10
+
11
+ expect(matcher.matches?(request)).to be_a TrueClass
12
+ end
13
+
14
+ it 'matches if the first subdomain is API' do
15
+ matcher = SubdomainMatcher.new
16
+ request = { 'HTTP_HOST' => 'api.matrix.example.com' }
17
+
18
+ expect(matcher.matches?(request)).to be_a TrueClass
19
+ end
20
+
21
+ it 'does not match if the first subdomain is not API' do
22
+ matcher = SubdomainMatcher.new
23
+ request = { 'HTTP_HOST' => 'matrix.example.com' }
24
+
25
+ expect(matcher.matches?(request)).to be_a FalseClass
26
+ end
27
+
28
+ it 'allows the matched subdomain to be specified' do
29
+ matcher = SubdomainMatcher.new(allowed_subdomains: 'matrix')
30
+ request = { 'HTTP_HOST' => 'matrix.example.com' }
31
+
32
+ expect(matcher.matches?(request)).to be_a TrueClass
33
+ end
34
+
35
+ it 'allows more than one subdomain to be matched' do
36
+ matcher = SubdomainMatcher.new(allowed_subdomains: %w{api matrix})
37
+
38
+ request = { 'HTTP_HOST' => 'matrix.example.com' }
39
+ expect(matcher.matches?(request)).to be_a TrueClass
40
+
41
+ request = { 'HTTP_HOST' => 'api.example.com' }
42
+ expect(matcher.matches?(request)).to be_a TrueClass
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,66 @@
1
+ require 'rspectacular'
2
+ require 'apill/matchers/version_matcher'
3
+
4
+ module Apill
5
+ module Matchers
6
+ describe VersionMatcher do
7
+ context 'when the version is passed in the accept header' do
8
+ it 'does not match if the subdomain is API but the requested version does not ' \
9
+ 'equal the version constraint' do
10
+
11
+ request = {
12
+ 'API_APPLICATION_NAME' => 'matrix',
13
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion;version=10.0',
14
+ }
15
+
16
+ matcher = VersionMatcher.new(version_constraint: '10.1')
17
+
18
+ expect(matcher.matches?(request)).to be_a FalseClass
19
+ end
20
+
21
+ it 'does match if the subdomain is API and the requested version equals the ' \
22
+ 'version constraint' do
23
+
24
+ request = {
25
+ 'API_APPLICATION_NAME' => 'matrix',
26
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion;version=10.0',
27
+ }
28
+
29
+ matcher = VersionMatcher.new(version_constraint: '10.0')
30
+
31
+ expect(matcher.matches?(request)).to be_a TrueClass
32
+ end
33
+ end
34
+
35
+ context 'when the version is not passed in the accept header' do
36
+ it 'does not match if the subdomain is API but the requested version does not ' \
37
+ 'equal the version constraint' do
38
+
39
+ request = {
40
+ 'API_APPLICATION_NAME' => 'matrix',
41
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion',
42
+ }
43
+
44
+ matcher = VersionMatcher.new(version_constraint: '10.1',
45
+ default_version: '10.0')
46
+
47
+ expect(matcher.matches?(request)).to be_a FalseClass
48
+ end
49
+
50
+ it 'does match if the subdomain is API and the requested version equals the ' \
51
+ 'version constraint' do
52
+
53
+ request = {
54
+ 'API_APPLICATION_NAME' => 'matrix',
55
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion',
56
+ }
57
+
58
+ matcher = VersionMatcher.new(version_constraint: '10.0',
59
+ default_version: '10.0')
60
+
61
+ expect(matcher.matches?(request)).to be_a TrueClass
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,114 @@
1
+ require 'rspectacular'
2
+ require 'apill/middleware/api_request'
3
+
4
+ HumanError.configure do |config|
5
+ config.api_error_documentation_url = 'http://error.com'
6
+ config.knowledgebase_url = 'http://knowledge.com'
7
+ config.api_version = '1'
8
+ end
9
+
10
+ Apill.configure do |config|
11
+ config.allowed_subdomains = %w{api}
12
+ config.application_name = 'matrix'
13
+ end
14
+
15
+ module Apill
16
+ module Middleware
17
+ describe ApiRequest do
18
+ let(:app) { lambda { |env| [200, {}, 'response'] } }
19
+
20
+ it 'does not allow requests if they are not for an allowed subdomain' do
21
+ api_request_middleware = ApiRequest.new(app)
22
+
23
+ request = {
24
+ 'HTTP_HOST' => 'notvalid.example.com',
25
+ 'HTTP_ACCEPT' => '',
26
+ 'QUERY_STRING' => 'first=my_param&accept=application/vnd.silent+zion;version=1.0.0',
27
+ 'API_APPLICATION_NAME' => 'matrix',
28
+ }
29
+
30
+ status, headers, response = api_request_middleware.call(request)
31
+
32
+ expect(status).to eql 404
33
+ expect(headers).to eql Hash.new
34
+ expect(response).to eql(
35
+ [
36
+ '{' \
37
+ '"error":' \
38
+ '{' \
39
+ '"status":404,' \
40
+ '"code":1010,' \
41
+ '"developer_documentation_uri":"http://error.com/1010?version=1",' \
42
+ '"customer_support_uri":"http://knowledge.com/1234567890",' \
43
+ '"developer_message_key":"errors.invalid.subdomain.error.developer",' \
44
+ '"developer_message":"The resource you attempted to access is either not authorized for the authenticated user or does not exist.",' \
45
+ '"developer_details":' \
46
+ '{' \
47
+ '"http_host":"notvalid.example.com"' \
48
+ '},' \
49
+ '"friendly_message_key":"errors.invalid.subdomain.error.friendly",' \
50
+ '"friendly_message":"Sorry! The resource you tried to access does not exist."' \
51
+ '}' \
52
+ '}'
53
+ ]
54
+ )
55
+ end
56
+
57
+ it 'does not allow requests if they are for an allowed subdomain but does ' \
58
+ 'not have a valid accept header' do
59
+
60
+ api_request_middleware = ApiRequest.new(app)
61
+
62
+ request = {
63
+ 'HTTP_HOST' => 'api.example.com',
64
+ 'HTTP_ACCEPT' => '',
65
+ 'QUERY_STRING' => 'first=my_param&accept=application/vnd.silent+zion;version=1.0.0',
66
+ 'API_APPLICATION_NAME' => 'matrix',
67
+ }
68
+
69
+ status, headers, response = api_request_middleware.call(request)
70
+
71
+ expect(status).to eql 400
72
+ expect(headers).to eql Hash.new
73
+ expect(response).to eql(
74
+ [
75
+ '{' \
76
+ '"error":' \
77
+ '{' \
78
+ '"status":400,' \
79
+ '"code":1007,' \
80
+ '"developer_documentation_uri":"http://error.com/1007?version=1",' \
81
+ '"customer_support_uri":"http://knowledge.com/1234567890",' \
82
+ '"developer_message_key":"errors.invalid.api.request.error.developer",' \
83
+ '"developer_message":"The accept header that you passed in the request cannot be parsed, please refer to the documentation to verify.",' \
84
+ '"developer_details":' \
85
+ '{' \
86
+ '"accept_header":""' \
87
+ '},' \
88
+ '"friendly_message_key":"errors.invalid.api.request.error.friendly",' \
89
+ '"friendly_message":"Sorry! We couldn\'t understand what you were trying to ask us to do."' \
90
+ '}' \
91
+ '}'
92
+ ]
93
+ )
94
+ end
95
+
96
+ it 'does allow requests if both the subdomain and the accept header are valid' do
97
+ api_request_middleware = ApiRequest.new(app)
98
+
99
+ request = {
100
+ 'HTTP_HOST' => 'api.example.com',
101
+ 'HTTP_ACCEPT' => 'application/vnd.matrix+zion;version=1.0.0',
102
+ 'QUERY_STRING' => 'first=my_param&accept=application/vnd.matrix+zion;version=1.0.0',
103
+ 'API_APPLICATION_NAME' => 'matrix',
104
+ }
105
+
106
+ status, headers, response = api_request_middleware.call(request)
107
+
108
+ expect(status).to eql 200
109
+ expect(headers).to eql Hash.new
110
+ expect(response).to eql 'response'
111
+ end
112
+ end
113
+ end
114
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apill
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - jfelchner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-28 00:00:00.000000000 Z
11
+ date: 2014-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: human_error
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.11'
19
+ version: '1.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.11'
26
+ version: '1.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,47 +44,66 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.38'
47
+ version: '0.50'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.38'
54
+ version: '0.50'
55
+ - !ruby/object:Gem::Dependency
56
+ name: codeclimate-test-reporter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.3.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.3.0
55
69
  description: ''
56
70
  email: accounts+git@thekompanee.com
57
71
  executables: []
58
72
  extensions: []
59
- extra_rdoc_files:
60
- - README.md
61
- - LICENSE
73
+ extra_rdoc_files: []
62
74
  files:
63
75
  - LICENSE
64
76
  - README.md
65
77
  - Rakefile
66
78
  - lib/apill.rb
67
79
  - lib/apill/accept_header.rb
80
+ - lib/apill/configuration.rb
68
81
  - lib/apill/errors/invalid_api_request_error.rb
82
+ - lib/apill/errors/invalid_subdomain_error.rb
69
83
  - lib/apill/matchers/accept_header_matcher.rb
70
84
  - lib/apill/matchers/generic_matcher.rb
71
85
  - lib/apill/matchers/invalid_api_request_matcher.rb
72
86
  - lib/apill/matchers/subdomain_matcher.rb
73
87
  - lib/apill/matchers/version_matcher.rb
88
+ - lib/apill/middleware/api_request.rb
74
89
  - lib/apill/responses/invalid_api_request_response.rb
90
+ - lib/apill/responses/invalid_subdomain_response.rb
75
91
  - lib/apill/version.rb
76
- - spec/lib/apill/accept_header_spec.rb
77
- - spec/lib/apill/errors/invalid_api_request_error_spec.rb
78
- - spec/lib/apill/matchers/accept_header_matcher_spec.rb
79
- - spec/lib/apill/matchers/invalid_api_request_matcher_spec.rb
80
- - spec/lib/apill/matchers/subdomain_matcher_spec.rb
81
- - spec/lib/apill/matchers/version_matcher_spec.rb
92
+ - spec/apill/accept_header_spec.rb
93
+ - spec/apill/errors/invalid_api_request_error_spec.rb
94
+ - spec/apill/errors/invalid_subdomain_error_spec.rb
95
+ - spec/apill/invalid_subdomain_response_spec.rb
96
+ - spec/apill/matchers/accept_header_matcher_spec.rb
97
+ - spec/apill/matchers/invalid_api_request_matcher_spec.rb
98
+ - spec/apill/matchers/subdomain_matcher_spec.rb
99
+ - spec/apill/matchers/version_matcher_spec.rb
100
+ - spec/apill/middleware/api_request_spec.rb
82
101
  homepage: https://github.com/jfelchner/apill
83
- licenses: []
102
+ licenses:
103
+ - MIT
84
104
  metadata: {}
85
105
  post_install_message:
86
- rdoc_options:
87
- - "--charset = UTF-8"
106
+ rdoc_options: []
88
107
  require_paths:
89
108
  - lib
90
109
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -98,16 +117,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
117
  - !ruby/object:Gem::Version
99
118
  version: '0'
100
119
  requirements: []
101
- rubyforge_project: apill
102
- rubygems_version: 2.3.0
120
+ rubyforge_project:
121
+ rubygems_version: 2.4.2
103
122
  signing_key:
104
123
  specification_version: 4
105
124
  summary: Common API functionality
106
125
  test_files:
107
- - spec/lib/apill/accept_header_spec.rb
108
- - spec/lib/apill/errors/invalid_api_request_error_spec.rb
109
- - spec/lib/apill/matchers/accept_header_matcher_spec.rb
110
- - spec/lib/apill/matchers/invalid_api_request_matcher_spec.rb
111
- - spec/lib/apill/matchers/subdomain_matcher_spec.rb
112
- - spec/lib/apill/matchers/version_matcher_spec.rb
126
+ - spec/apill/accept_header_spec.rb
127
+ - spec/apill/errors/invalid_api_request_error_spec.rb
128
+ - spec/apill/errors/invalid_subdomain_error_spec.rb
129
+ - spec/apill/invalid_subdomain_response_spec.rb
130
+ - spec/apill/matchers/accept_header_matcher_spec.rb
131
+ - spec/apill/matchers/invalid_api_request_matcher_spec.rb
132
+ - spec/apill/matchers/subdomain_matcher_spec.rb
133
+ - spec/apill/matchers/version_matcher_spec.rb
134
+ - spec/apill/middleware/api_request_spec.rb
113
135
  has_rdoc:
@@ -1,92 +0,0 @@
1
- require 'ostruct'
2
- require 'rspectacular'
3
- require 'apill/matchers/accept_header_matcher'
4
-
5
- module Apill
6
- module Matchers
7
- describe AcceptHeaderMatcher do
8
- it 'matches if the subdomain is API and the accept header is valid' do
9
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion;version=1.0.0' },
10
- params: {},
11
- subdomains: [ 'api' ])
12
-
13
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
14
-
15
- expect(matcher.matches?(request)).to be_truthy
16
- end
17
-
18
- it 'matches if the subdomain is API and the accept header is passed in as a parameter' do
19
- request = OpenStruct.new(headers: {},
20
- params: { 'accept' => 'application/vnd.matrix+zion;version=1.0.0' },
21
- subdomains: [ 'api' ])
22
-
23
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
24
-
25
- expect(matcher.matches?(request)).to be_truthy
26
- end
27
-
28
- it 'matches the header accept header if the subdomain is API and the accept header is passed both as a valid header and as a parameter' do
29
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion;version=1.0.0' },
30
- params: { 'accept' => 'application/vnd.matrix+zion;version=2.0.0' },
31
- subdomains: [ 'api' ])
32
-
33
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
34
- matcher.matches?(request)
35
-
36
- expect(matcher.accept_header.version).to eql '1.0.0'
37
- end
38
-
39
- it "matches the parameter's accept header if the subdomain is API and the accept header is passed both as an invalid header as well as as a parameter" do
40
- request = OpenStruct.new(headers: { 'Accept' => 'application/vndmatrix+zion;version=1.0.0' },
41
- params: { 'accept' => 'application/vnd.matrix+zion;version=2.0.0' },
42
- subdomains: [ 'api' ])
43
-
44
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
45
- matcher.matches?(request)
46
-
47
- expect(matcher.accept_header.version).to eql '2.0.0'
48
- end
49
-
50
- it "matches the parameter's accept header if the subdomain is API and the accept header is passed both as an invalid header as well as as a parameter" do
51
- request = OpenStruct.new(headers: { 'Accept' => 'application/vndmatrix+zion;version=1.0.0' },
52
- params: {},
53
- subdomains: [ 'api' ])
54
-
55
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
56
- matcher.matches?(request)
57
-
58
- expect(matcher.accept_header.raw_accept_header).to eql 'application/vndmatrix+zion;version=1.0.0'
59
- end
60
-
61
- it 'does not match if the subdomain is not API but the accept header is valid' do
62
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion' },
63
- params: {},
64
- subdomains: [ 'not-api' ])
65
-
66
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
67
-
68
- expect(matcher.matches?(request)).to be_falsey
69
- end
70
-
71
- it 'does not match if the subdomain is API but the accept header is invalid' do
72
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.' },
73
- params: {},
74
- subdomains: [ 'api' ])
75
-
76
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
77
-
78
- expect(matcher.matches?(request)).to be_falsey
79
- end
80
-
81
- it 'does not match if neither the subdomain is API nor the accept header is valid' do
82
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.' },
83
- params: {},
84
- subdomains: [ 'not-api' ])
85
-
86
- matcher = AcceptHeaderMatcher.new(application: 'matrix')
87
-
88
- expect(matcher.matches?(request)).to be_falsey
89
- end
90
- end
91
- end
92
- end
@@ -1,19 +0,0 @@
1
- require 'ostruct'
2
- require 'rspectacular'
3
- require 'apill/matchers/invalid_api_request_matcher'
4
-
5
- module Apill
6
- module Matchers
7
- describe InvalidApiRequestMatcher do
8
- it 'is the inverse of whether the accept header matches' do
9
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion;version=1.0.0' },
10
- subdomains: [ 'api' ],
11
- params: {})
12
-
13
- matcher = InvalidApiRequestMatcher.new(application: 'matrix')
14
-
15
- expect(matcher.matches?(request)).to be_falsey
16
- end
17
- end
18
- end
19
- end
@@ -1,41 +0,0 @@
1
- require 'ostruct'
2
- require 'rspectacular'
3
- require 'apill/matchers/subdomain_matcher'
4
-
5
- module Apill
6
- module Matchers
7
- describe SubdomainMatcher do
8
- it 'matches if the subdomain is API' do
9
- request = OpenStruct.new(subdomains: [ 'api' ])
10
-
11
- matcher = SubdomainMatcher.new
12
-
13
- expect(matcher.matches?(request)).to be_truthy
14
- end
15
-
16
- it 'matches if the first subdomain is API' do
17
- request = OpenStruct.new(subdomains: [ 'api', 'matrix' ])
18
-
19
- matcher = SubdomainMatcher.new
20
-
21
- expect(matcher.matches?(request)).to be_truthy
22
- end
23
-
24
- it 'does not match if the first subdomain is not API' do
25
- request = OpenStruct.new(subdomains: [ 'matrix' ])
26
-
27
- matcher = SubdomainMatcher.new
28
-
29
- expect(matcher.matches?(request)).to be_falsey
30
- end
31
-
32
- it 'allows the matched subdomain to be specified' do
33
- request = OpenStruct.new(subdomains: [ 'matrix' ])
34
-
35
- matcher = SubdomainMatcher.new(subdomain: 'matrix')
36
-
37
- expect(matcher.matches?(request)).to be_truthy
38
- end
39
- end
40
- end
41
- end
@@ -1,92 +0,0 @@
1
- require 'ostruct'
2
- require 'rspectacular'
3
- require 'apill/matchers/version_matcher'
4
-
5
- module Apill
6
- module Matchers
7
- describe VersionMatcher do
8
- it 'raises an error when the accept header is not valid' do
9
- request = OpenStruct.new(headers: { 'Accept' => 'matrix' },
10
- subdomains: [ 'api' ],
11
- params: {})
12
-
13
- matcher = VersionMatcher.new
14
-
15
- expect { matcher.matches?(request) }.to raise_error Apill::Errors::InvalidApiRequestError
16
- end
17
-
18
- context 'when the version is passed in the accept header' do
19
- it 'does not match if the subdomain is not API but the request version is equal to the version constraint' do
20
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion;version=10.0' },
21
- subdomains: [ 'not-api' ],
22
- params: {})
23
-
24
- matcher = VersionMatcher.new(application: 'matrix',
25
- version_constraint: '10.0')
26
-
27
- expect(matcher.matches?(request)).to be_falsey
28
- end
29
-
30
- it 'does not match if the subdomain is API but the requested version does not equal the version constraint' do
31
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion;version=10.0' },
32
- subdomains: [ 'api' ],
33
- params: {})
34
-
35
- matcher = VersionMatcher.new(application: 'matrix',
36
- version_constraint: '10.1')
37
-
38
- expect(matcher.matches?(request)).to be_falsey
39
- end
40
-
41
- it 'does match if the subdomain is API and the requested version equals the version constraint' do
42
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion;version=10.0' },
43
- subdomains: [ 'api' ],
44
- params: {})
45
-
46
- matcher = VersionMatcher.new(application: 'matrix',
47
- version_constraint: '10.0')
48
-
49
- expect(matcher.matches?(request)).to be_truthy
50
- end
51
- end
52
-
53
- context 'when the version is not passed in the accept header' do
54
- it 'does not match if the subdomain is not API but the request version is equal to the version constraint' do
55
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion' },
56
- subdomains: [ 'not-api' ],
57
- params: {})
58
-
59
- matcher = VersionMatcher.new(application: 'matrix',
60
- version_constraint: '10.0',
61
- default_version: '10.0')
62
-
63
- expect(matcher.matches?(request)).to be_falsey
64
- end
65
-
66
- it 'does not match if the subdomain is API but the requested version does not equal the version constraint' do
67
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion' },
68
- subdomains: [ 'api' ],
69
- params: {})
70
-
71
- matcher = VersionMatcher.new(application: 'matrix',
72
- version_constraint: '10.1',
73
- default_version: '10.0')
74
-
75
- expect(matcher.matches?(request)).to be_falsey
76
- end
77
-
78
- it 'does match if the subdomain is API and the requested version equals the version constraint' do
79
- request = OpenStruct.new(headers: { 'Accept' => 'application/vnd.matrix+zion' },
80
- subdomains: [ 'api' ],
81
- params: {})
82
-
83
- matcher = VersionMatcher.new(application: 'matrix',
84
- version_constraint: '10.0',
85
- default_version: '10.0')
86
-
87
- expect(matcher.matches?(request)).to be_truthy
88
- end
89
- end
90
- end
91
- end
92
- end