apill 2.9.0 → 3.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 +4 -4
- data/lib/apill/errors/invalid_api_request_error.rb +9 -7
- data/lib/apill/errors/invalid_subdomain_error.rb +9 -7
- data/lib/apill/parameters/filter.rb +23 -0
- data/lib/apill/parameters/index.rb +17 -0
- data/lib/apill/parameters/page.rb +22 -0
- data/lib/apill/parameters/sort.rb +27 -0
- data/lib/apill/resource/model.rb +42 -0
- data/lib/apill/resource/processors/filtering.rb +63 -0
- data/lib/apill/resource/processors/indexing.rb +32 -0
- data/lib/apill/resource/processors/paging.rb +39 -0
- data/lib/apill/resource/processors/sorting.rb +35 -0
- data/lib/apill/resource.rb +68 -0
- data/lib/apill/version.rb +1 -1
- data/lib/apill.rb +1 -6
- data/spec/apill/errors/invalid_api_request_error_spec.rb +8 -17
- data/spec/apill/errors/invalid_subdomain_error_spec.rb +6 -15
- data/spec/apill/invalid_subdomain_response_spec.rb +31 -35
- data/spec/apill/middleware/api_request_spec.rb +47 -54
- data/spec/apill/resource/model_spec.rb +66 -0
- data/spec/apill/resource/processors/filtering_spec.rb +84 -0
- data/spec/apill/resource/processors/indexing_spec.rb +11 -0
- data/spec/apill/resource/processors/paging_spec.rb +54 -0
- data/spec/apill/resource/processors/sorting_spec.rb +74 -0
- metadata +24 -22
- data/lib/apill/mixins/indexable.rb +0 -49
- data/lib/apill/mixins/pageable.rb +0 -51
- data/lib/apill/mixins/queryable.rb +0 -86
- data/lib/apill/mixins/sortable.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c68aa89f529dc07063d53a0f2e53e210b606c1bb
|
4
|
+
data.tar.gz: 6fc4b20a268b92b45d8e2e3f0b6ae195b118827d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02544a5f552ca8205ccd6aff687b60460f001c06b49db580ebfc9ad2cc841e5af9af65440af2494d1ba3f4baf6c56b002d68ce1afc720b90c5b0192d62d7c509
|
7
|
+
data.tar.gz: 9ac237ebc9b635993885a286f8b22353c98c655efc68fde7c50056dba9cbc332298465e328bcc80c8cf23cc6a0f44eab59c21aa381943c06a43c54993fe8a4a5
|
@@ -2,25 +2,27 @@ require 'human_error'
|
|
2
2
|
|
3
3
|
module Apill
|
4
4
|
module Errors
|
5
|
-
class InvalidApiRequestError <
|
5
|
+
class InvalidApiRequestError < RuntimeError
|
6
|
+
include HumanError::Error
|
7
|
+
|
6
8
|
attr_accessor :accept_header
|
7
9
|
|
8
10
|
def http_status
|
9
11
|
400
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
14
|
+
def title
|
15
|
+
'Invalid API Request'
|
16
|
+
end
|
17
|
+
|
18
|
+
def detail
|
13
19
|
'The accept header that you passed in the request cannot be parsed, ' \
|
14
20
|
'please refer to the documentation to verify.'
|
15
21
|
end
|
16
22
|
|
17
|
-
def
|
23
|
+
def source
|
18
24
|
{ accept_header: accept_header }
|
19
25
|
end
|
20
|
-
|
21
|
-
def friendly_message
|
22
|
-
"Sorry! We couldn't understand what you were trying to ask us to do."
|
23
|
-
end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -2,25 +2,27 @@ require 'human_error'
|
|
2
2
|
|
3
3
|
module Apill
|
4
4
|
module Errors
|
5
|
-
class InvalidSubdomainError <
|
5
|
+
class InvalidSubdomainError < RuntimeError
|
6
|
+
include HumanError::Error
|
7
|
+
|
6
8
|
attr_accessor :http_host
|
7
9
|
|
8
10
|
def http_status
|
9
11
|
404
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
14
|
+
def title
|
15
|
+
'Invalid Subdomain'
|
16
|
+
end
|
17
|
+
|
18
|
+
def detail
|
13
19
|
'The resource you attempted to access is either not authorized for the ' \
|
14
20
|
'authenticated user or does not exist.'
|
15
21
|
end
|
16
22
|
|
17
|
-
def
|
23
|
+
def source
|
18
24
|
{ http_host: http_host }
|
19
25
|
end
|
20
|
-
|
21
|
-
def friendly_message
|
22
|
-
'Sorry! The resource you tried to access does not exist.'
|
23
|
-
end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Apill
|
2
|
+
class Parameters
|
3
|
+
class Filter
|
4
|
+
attr_accessor :raw_parameters
|
5
|
+
|
6
|
+
def initialize(raw_parameters)
|
7
|
+
self.raw_parameters = raw_parameters
|
8
|
+
end
|
9
|
+
|
10
|
+
def each_with_object(memoized)
|
11
|
+
raw_parameters.each do |raw_parameter|
|
12
|
+
next if raw_parameter[0] == 'query' ||
|
13
|
+
raw_parameter[1] == '' ||
|
14
|
+
raw_parameter[1].nil?
|
15
|
+
|
16
|
+
memoized = yield raw_parameter[0], raw_parameter[1], memoized
|
17
|
+
end
|
18
|
+
|
19
|
+
memoized
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Apill
|
2
|
+
class Parameters
|
3
|
+
class Index
|
4
|
+
DEFAULT_QUERY = '*'
|
5
|
+
|
6
|
+
attr_accessor :raw_parameters
|
7
|
+
|
8
|
+
def initialize(raw_parameters)
|
9
|
+
self.raw_parameters = raw_parameters
|
10
|
+
end
|
11
|
+
|
12
|
+
def query
|
13
|
+
raw_parameters['query'] || raw_parameters['q']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Apill
|
2
|
+
class Parameters
|
3
|
+
class Page
|
4
|
+
DEFAULT_STARTING_PAGE = 1
|
5
|
+
DEFAULT_PAGE_SIZE = 25
|
6
|
+
|
7
|
+
attr_accessor :raw_parameters
|
8
|
+
|
9
|
+
def initialize(raw_parameters)
|
10
|
+
self.raw_parameters = raw_parameters
|
11
|
+
end
|
12
|
+
|
13
|
+
def page_number
|
14
|
+
raw_parameters['number'] || DEFAULT_STARTING_PAGE
|
15
|
+
end
|
16
|
+
|
17
|
+
def per_page
|
18
|
+
raw_parameters['size'] || DEFAULT_PAGE_SIZE
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Apill
|
2
|
+
class Parameters
|
3
|
+
class Sort
|
4
|
+
DESCENDING_PREFIX = '-'
|
5
|
+
|
6
|
+
attr_accessor :raw_parameters
|
7
|
+
|
8
|
+
def initialize(raw_parameters)
|
9
|
+
self.raw_parameters = raw_parameters ? raw_parameters.split(',') : ['-created_at']
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
@to_h ||= Hash[to_a]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_a
|
17
|
+
@to_a ||= raw_parameters.map do |raw_parameter|
|
18
|
+
if raw_parameter.start_with?(DESCENDING_PREFIX)
|
19
|
+
[raw_parameter[1..-1], 'desc']
|
20
|
+
else
|
21
|
+
[raw_parameter, 'asc']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'apill/resource/processors/filtering'
|
2
|
+
require 'apill/resource/processors/sorting'
|
3
|
+
require 'apill/resource/processors/paging'
|
4
|
+
require 'apill/resource/processors/indexing'
|
5
|
+
|
6
|
+
module Apill
|
7
|
+
module Resource
|
8
|
+
class Model
|
9
|
+
DEFAULT_PROCESSORS = %w{filtering sorting paging indexing}
|
10
|
+
|
11
|
+
attr_accessor :resource,
|
12
|
+
:parameters,
|
13
|
+
:processors
|
14
|
+
|
15
|
+
def initialize(resource:, parameters:, **options)
|
16
|
+
self.resource = resource
|
17
|
+
self.parameters = parameters
|
18
|
+
self.processors = options.fetch(:processors, DEFAULT_PROCESSORS)
|
19
|
+
end
|
20
|
+
|
21
|
+
def processed
|
22
|
+
@processed ||= \
|
23
|
+
processors.inject(resource) do |processed_resource, processor|
|
24
|
+
processor.processed(processed_resource, parameters)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def meta
|
29
|
+
@meta ||= \
|
30
|
+
processors.inject({}) do |metadata, processor|
|
31
|
+
metadata.merge processor.meta(processed, parameters)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def processors=(other)
|
36
|
+
@processors = other.map do |processor|
|
37
|
+
Object.const_get "::Apill::Resource::Processors::#{processor.capitalize}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'apill/parameters/filter'
|
2
|
+
|
3
|
+
module Apill
|
4
|
+
module Resource
|
5
|
+
module Processors
|
6
|
+
class Filtering
|
7
|
+
attr_accessor :resource,
|
8
|
+
:parameters
|
9
|
+
|
10
|
+
def initialize(resource, parameters = {})
|
11
|
+
self.resource = resource
|
12
|
+
self.parameters = Parameters::Filter.new(parameters['filter'] || {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.processed(*attrs)
|
16
|
+
new(*attrs).processed
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.meta(*_attrs)
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
|
23
|
+
def processed
|
24
|
+
parameters.each_with_object(resource) do |name, value, filtered_resource|
|
25
|
+
filter_method = filter_method_for(name)
|
26
|
+
|
27
|
+
if !filter_method
|
28
|
+
filtered_resource
|
29
|
+
elsif filter_method.arity == 0
|
30
|
+
filtered_resource.public_send(filter_method.name)
|
31
|
+
else
|
32
|
+
filtered_resource.public_send(filter_method.name, value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def filter_method_for(filter_item)
|
40
|
+
filter_method_name = filter_method_name_for(filter_item)
|
41
|
+
|
42
|
+
resource_class.method(filter_method_name) if filter_method_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def filter_method_name_for(filter_item)
|
46
|
+
if resource_class.respond_to? "for_#{filter_item}"
|
47
|
+
"for_#{filter_item}"
|
48
|
+
elsif resource_class.respond_to? filter_item
|
49
|
+
filter_item
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def resource_class
|
54
|
+
@resource_class ||= if resource.respond_to? :klass
|
55
|
+
resource.klass
|
56
|
+
else
|
57
|
+
resource
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'apill/parameters/index'
|
2
|
+
|
3
|
+
module Apill
|
4
|
+
module Resource
|
5
|
+
module Processors
|
6
|
+
class Indexing
|
7
|
+
attr_accessor :resource,
|
8
|
+
:parameters
|
9
|
+
|
10
|
+
def initialize(resource, parameters)
|
11
|
+
self.resource = resource
|
12
|
+
self.parameters = Parameters::Index.new(parameters['filter'] || {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.processed(*attrs)
|
16
|
+
new(*attrs).processed
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.meta(*_attrs)
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
|
23
|
+
def processed
|
24
|
+
return resource unless resource.respond_to?(:for_query) &&
|
25
|
+
parameters.query
|
26
|
+
|
27
|
+
resource.for_query(parameters.query)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'apill/parameters/page'
|
2
|
+
|
3
|
+
module Apill
|
4
|
+
module Resource
|
5
|
+
module Processors
|
6
|
+
class Paging
|
7
|
+
attr_accessor :resource,
|
8
|
+
:parameters
|
9
|
+
|
10
|
+
def initialize(resource, parameters = {})
|
11
|
+
self.resource = resource
|
12
|
+
self.parameters = Parameters::Page.new(parameters['page'] || {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.processed(*attrs)
|
16
|
+
new(*attrs).processed
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.meta(*attrs)
|
20
|
+
new(*attrs).meta
|
21
|
+
end
|
22
|
+
|
23
|
+
def processed
|
24
|
+
resource.page(parameters.page_number).
|
25
|
+
per(parameters.per_page)
|
26
|
+
end
|
27
|
+
|
28
|
+
def meta
|
29
|
+
{
|
30
|
+
'total-pages' => processed.total_pages,
|
31
|
+
'current-page' => processed.current_page,
|
32
|
+
'previous-page' => processed.prev_page,
|
33
|
+
'next-page' => processed.next_page,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'apill/parameters/sort'
|
2
|
+
|
3
|
+
module Apill
|
4
|
+
module Resource
|
5
|
+
module Processors
|
6
|
+
class Sorting
|
7
|
+
attr_accessor :resource,
|
8
|
+
:parameters
|
9
|
+
|
10
|
+
def initialize(resource, parameters = {})
|
11
|
+
self.resource = resource
|
12
|
+
self.parameters = Parameters::Sort.new(parameters['sort'])
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.processed(*attrs)
|
16
|
+
new(*attrs).processed
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.meta(*attrs)
|
20
|
+
new(*attrs).meta
|
21
|
+
end
|
22
|
+
|
23
|
+
def processed
|
24
|
+
resource.order(parameters.to_h)
|
25
|
+
end
|
26
|
+
|
27
|
+
def meta
|
28
|
+
{
|
29
|
+
'sort' => parameters.to_h,
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'apill/resource/model'
|
2
|
+
require 'human_error/rescuable_resource'
|
3
|
+
|
4
|
+
module Apill
|
5
|
+
module Resource
|
6
|
+
module ClassMethods
|
7
|
+
def plural_resource_name
|
8
|
+
name[/(\w+)Controller\z/, 1].
|
9
|
+
underscore.
|
10
|
+
pluralize.
|
11
|
+
downcase
|
12
|
+
end
|
13
|
+
|
14
|
+
def singular_resource_name
|
15
|
+
name[/(\w+)Controller\z/, 1].
|
16
|
+
underscore.
|
17
|
+
singularize.
|
18
|
+
downcase
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(base)
|
23
|
+
base.extend ClassMethods
|
24
|
+
base.include HumanError::RescuableResource
|
25
|
+
base.include HumanError::VerifiableResource
|
26
|
+
end
|
27
|
+
|
28
|
+
def api_resource
|
29
|
+
@resource ||= Resource::Model.new(
|
30
|
+
resource: public_send(self.class.plural_resource_name),
|
31
|
+
parameters: api_resource_params)
|
32
|
+
end
|
33
|
+
|
34
|
+
def api_resource_params
|
35
|
+
params.permit(sort: String,
|
36
|
+
page: %i{
|
37
|
+
number
|
38
|
+
size
|
39
|
+
offset
|
40
|
+
limit
|
41
|
+
cursor
|
42
|
+
},
|
43
|
+
filter: api_filterable_parameters)
|
44
|
+
end
|
45
|
+
|
46
|
+
def api_filterable_parameters
|
47
|
+
@api_filterable_parameters ||= begin
|
48
|
+
filter_params = params.fetch(:filter, {})
|
49
|
+
scalar_params = [:query]
|
50
|
+
array_params = {}
|
51
|
+
|
52
|
+
api_filterable_attributes.each do |api_filterable_attribute|
|
53
|
+
if filter_params[api_filterable_attribute].class == Array
|
54
|
+
array_params[api_filterable_attribute] = []
|
55
|
+
else
|
56
|
+
scalar_params << api_filterable_attribute
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
scalar_params << array_params
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def api_filterable_attributes
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/apill/version.rb
CHANGED
data/lib/apill.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
require 'kaminari'
|
2
|
-
|
3
1
|
require 'apill/version'
|
4
2
|
|
5
3
|
require 'apill/configuration'
|
6
4
|
require 'apill/matchers/accept_header_matcher'
|
7
5
|
require 'apill/matchers/subdomain_matcher'
|
8
6
|
require 'apill/matchers/version_matcher'
|
9
|
-
require 'apill/
|
10
|
-
require 'apill/mixins/sortable'
|
11
|
-
require 'apill/mixins/queryable'
|
12
|
-
require 'apill/mixins/pageable'
|
7
|
+
require 'apill/resource'
|
13
8
|
require 'apill/serializers/json_api'
|
14
9
|
|
15
10
|
require 'apill/middleware/api_request'
|
@@ -10,29 +10,20 @@ describe InvalidApiRequestError do
|
|
10
10
|
expect(error.http_status).to eql 400
|
11
11
|
end
|
12
12
|
|
13
|
-
it 'has a code
|
14
|
-
expect(error.code).to eql
|
13
|
+
it 'has a code' do
|
14
|
+
expect(error.code).to eql 'errors.invalid_api_request_error'
|
15
15
|
end
|
16
16
|
|
17
|
-
it '
|
18
|
-
expect(error.
|
17
|
+
it 'can output the detail' do
|
18
|
+
expect(error.detail).to eql 'The accept header that you passed in the ' \
|
19
|
+
'request cannot be parsed, please refer to ' \
|
20
|
+
'the documentation to verify.'
|
19
21
|
end
|
20
22
|
|
21
|
-
it 'can output the
|
22
|
-
expect(error.developer_message).to eql 'The accept header that you passed in the ' \
|
23
|
-
'request cannot be parsed, please refer to ' \
|
24
|
-
'the documentation to verify.'
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'can output the developer details' do
|
23
|
+
it 'can output the source' do
|
28
24
|
error = InvalidApiRequestError.new accept_header: 'foo'
|
29
25
|
|
30
|
-
expect(error.
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'can output the friendly message' do
|
34
|
-
expect(error.friendly_message).to eql "Sorry! We couldn't understand what you were " \
|
35
|
-
'trying to ask us to do.'
|
26
|
+
expect(error.source).to eql(accept_header: 'foo')
|
36
27
|
end
|
37
28
|
end
|
38
29
|
end
|
@@ -10,29 +10,20 @@ describe InvalidSubdomainError do
|
|
10
10
|
expect(error.http_status).to eql 404
|
11
11
|
end
|
12
12
|
|
13
|
-
it 'has a code
|
14
|
-
expect(error.code).to eql
|
13
|
+
it 'has a code' do
|
14
|
+
expect(error.code).to eql 'errors.invalid_subdomain_error'
|
15
15
|
end
|
16
16
|
|
17
|
-
it '
|
18
|
-
expect(error.
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'can output the developer message' do
|
22
|
-
expect(error.developer_message).to eql \
|
17
|
+
it 'can output the detail' do
|
18
|
+
expect(error.detail).to eql \
|
23
19
|
'The resource you attempted to access is either not authorized for the ' \
|
24
20
|
'authenticated user or does not exist.'
|
25
21
|
end
|
26
22
|
|
27
|
-
it 'can output the
|
23
|
+
it 'can output the source' do
|
28
24
|
error = InvalidSubdomainError.new http_host: 'foo'
|
29
25
|
|
30
|
-
expect(error.
|
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.'
|
26
|
+
expect(error.source).to eql(http_host: 'foo')
|
36
27
|
end
|
37
28
|
end
|
38
29
|
end
|
@@ -3,43 +3,39 @@ require 'apill/responses/invalid_subdomain_response'
|
|
3
3
|
|
4
4
|
module Apill
|
5
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
|
-
|
6
|
+
describe InvalidSubdomainResponse, singletons: HumanError::Configuration do
|
13
7
|
it 'returns the proper response' do
|
14
|
-
|
15
|
-
|
8
|
+
HumanError.configuration.url_mappings = {
|
9
|
+
'external_documentation_urls' => {
|
10
|
+
'errors.invalid_subdomain_response' => 'http://example.com/foo',
|
11
|
+
},
|
12
|
+
'developer_documentation_urls' => {
|
13
|
+
'errors.invalid_subdomain_response' => 'http://example.com/foo',
|
14
|
+
},
|
15
|
+
}
|
16
|
+
|
17
|
+
request = { 'HTTP_HOST' => 'api.example.com' }
|
18
|
+
status, headers, response = InvalidSubdomainResponse.call(request)
|
16
19
|
|
17
|
-
expect(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
'
|
23
|
-
|
24
|
-
'
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
'},' \
|
37
|
-
'"friendly_message_key":"errors.invalid.subdomain.error.friendly",' \
|
38
|
-
'"friendly_message":"Sorry! The resource you tried to access does not ' \
|
39
|
-
'exist."' \
|
40
|
-
'}' \
|
41
|
-
'}',
|
42
|
-
],
|
20
|
+
expect(status).to eql 404
|
21
|
+
expect(headers).to eql({})
|
22
|
+
expect(JSON.load(response[0])).to include(
|
23
|
+
'errors' => [
|
24
|
+
include(
|
25
|
+
'id' => match(/[a-f0-9\-]+/),
|
26
|
+
'links' => {
|
27
|
+
'about' => nil,
|
28
|
+
'documentation' => nil,
|
29
|
+
},
|
30
|
+
'status' => 404,
|
31
|
+
'code' => 'errors.invalid_subdomain_error',
|
32
|
+
'title' => 'Invalid Subdomain',
|
33
|
+
'detail' => 'The resource you attempted to access is either not authorized ' \
|
34
|
+
'for the authenticated user or does not exist.',
|
35
|
+
'source' => {
|
36
|
+
'http_host' => 'api.example.com',
|
37
|
+
},
|
38
|
+
),
|
43
39
|
],
|
44
40
|
)
|
45
41
|
end
|