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.
@@ -3,15 +3,18 @@ require 'apill/middleware/api_request'
3
3
 
4
4
  module Apill
5
5
  module Middleware
6
- describe ApiRequest do
6
+ describe ApiRequest, singletons: HumanError::Configuration do
7
7
  let(:app) { ->(_env) { [200, {}, 'response'] } }
8
8
 
9
9
  before(:each) do
10
- HumanError.configure do |config|
11
- config.api_error_documentation_url = 'http://error.com'
12
- config.knowledgebase_url = 'http://knowledge.com'
13
- config.api_version = '1'
14
- end
10
+ HumanError.configuration.url_mappings = {
11
+ 'external_documentation_urls' => {
12
+ 'errors.invalid_subdomain_response' => 'http://example.com/foo',
13
+ },
14
+ 'developer_documentation_urls' => {
15
+ 'errors.invalid_subdomain_response' => 'http://example.com/foo',
16
+ },
17
+ }
15
18
 
16
19
  Apill.configure do |config|
17
20
  config.allowed_subdomains = %w{api matrix}
@@ -47,30 +50,25 @@ describe ApiRequest do
47
50
 
48
51
  status, headers, response = api_request_middleware.call(request)
49
52
 
50
- expect(status).to eql 404
51
- expect(headers).to eql({})
52
- expect(response).to eql(
53
- [
54
- '{' \
55
- '"error":' \
56
- '{' \
57
- '"status":404,' \
58
- '"code":1010,' \
59
- '"developer_documentation_uri":"http://error.com/1010?version=1",' \
60
- '"customer_support_uri":"http://knowledge.com/1234567890",' \
61
- '"developer_message_key":"errors.invalid.subdomain.error.developer",' \
62
- '"developer_message":"The resource you attempted to access is either not ' \
63
- 'authorized for the authenticated user or does not ' \
64
- 'exist.",' \
65
- '"developer_details":' \
66
- '{' \
67
- '"http_host":"notvalid.example.com"' \
68
- '},' \
69
- '"friendly_message_key":"errors.invalid.subdomain.error.friendly",' \
70
- '"friendly_message":"Sorry! The resource you tried to access does not ' \
71
- 'exist."' \
72
- '}' \
73
- '}',
53
+ expect(status).to eql 404
54
+ expect(headers).to eql({})
55
+ expect(JSON.load(response[0])).to include(
56
+ 'errors' => [
57
+ {
58
+ 'id' => match(/[a-z0-9\-]+/),
59
+ 'links' => {
60
+ 'about' => nil,
61
+ 'documentation' => nil,
62
+ },
63
+ 'status' => 404,
64
+ 'code' => 'errors.invalid_subdomain_error',
65
+ 'title' => 'Invalid Subdomain',
66
+ 'detail' => 'The resource you attempted to access is either not authorized ' \
67
+ 'for the authenticated user or does not exist.',
68
+ 'source' => {
69
+ 'http_host' => 'notvalid.example.com',
70
+ },
71
+ },
74
72
  ],
75
73
  )
76
74
  end
@@ -88,30 +86,25 @@ describe ApiRequest do
88
86
 
89
87
  status, headers, response = api_request_middleware.call(request)
90
88
 
91
- expect(status).to eql 400
92
- expect(headers).to eql({})
93
- expect(response).to eql(
94
- [
95
- '{' \
96
- '"error":' \
97
- '{' \
98
- '"status":400,' \
99
- '"code":1007,' \
100
- '"developer_documentation_uri":"http://error.com/1007?version=1",' \
101
- '"customer_support_uri":"http://knowledge.com/1234567890",' \
102
- '"developer_message_key":"errors.invalid.api.request.error.developer",' \
103
- '"developer_message":"The accept header that you passed in the request ' \
104
- 'cannot be parsed, please refer to the documentation ' \
105
- 'to verify.",' \
106
- '"developer_details":' \
107
- '{' \
108
- '"accept_header":""' \
109
- '},' \
110
- '"friendly_message_key":"errors.invalid.api.request.error.friendly",' \
111
- '"friendly_message":"Sorry! We couldn\'t understand what you were trying ' \
112
- 'to ask us to do."' \
113
- '}' \
114
- '}',
89
+ expect(status).to eql 400
90
+ expect(headers).to eql({})
91
+ expect(JSON.load(response[0])).to include(
92
+ 'errors' => [
93
+ {
94
+ 'id' => match(/[a-z0-9\-]+/),
95
+ 'links' => {
96
+ 'about' => nil,
97
+ 'documentation' => nil,
98
+ },
99
+ 'status' => 400,
100
+ 'code' => 'errors.invalid_api_request_error',
101
+ 'title' => 'Invalid API Request',
102
+ 'detail' => 'The accept header that you passed in the request cannot be ' \
103
+ 'parsed, please refer to the documentation to verify.',
104
+ 'source' => {
105
+ 'accept_header' => '',
106
+ },
107
+ },
115
108
  ],
116
109
  )
117
110
  end
@@ -0,0 +1,66 @@
1
+ require 'rspectacular'
2
+ require 'apill/resource/model'
3
+
4
+ module Apill
5
+ module Resource
6
+ describe Model do
7
+ it 'can chain multiple processors together' do
8
+ resource = double
9
+ processed_resource = double
10
+
11
+ model = Model.new(resource: resource,
12
+ parameters: {
13
+ 'filter' => {
14
+ 'query' => 'my_query',
15
+ 'single_arity' => true,
16
+ 'multiple_arity' => 'multi',
17
+ },
18
+ 'sort' => 'my_attribute',
19
+ 'page' => {
20
+ 'number' => 10,
21
+ 'size' => 100,
22
+ },
23
+ })
24
+
25
+ allow(resource).to receive(:single_arity).
26
+ and_return(resource)
27
+ allow(resource).to receive(:multiple_arity).
28
+ with('multi').
29
+ and_return(resource)
30
+ allow(resource).to receive(:order).
31
+ with('my_attribute' => 'asc').
32
+ and_return(resource)
33
+ allow(resource).to receive(:page).
34
+ with(10).
35
+ and_return(resource)
36
+ allow(resource).to receive(:per).
37
+ with(100).
38
+ and_return(resource)
39
+ allow(resource).to receive(:for_query).
40
+ with('my_query').
41
+ and_return(processed_resource)
42
+ allow(resource).to receive(:total_pages).
43
+ and_return(10)
44
+ allow(resource).to receive(:current_page).
45
+ and_return(5)
46
+ allow(resource).to receive(:prev_page).
47
+ and_return(4)
48
+ allow(resource).to receive(:next_page).
49
+ and_return(6)
50
+
51
+ allow(processed_resource).to receive(:page).
52
+ with(10).
53
+ and_return(resource)
54
+
55
+ expect(model.processed).to eql processed_resource
56
+ expect(model.meta).to eql(
57
+ 'current-page' => 5,
58
+ 'total-pages' => 10,
59
+ 'previous-page' => 4,
60
+ 'next-page' => 6,
61
+ 'sort' => { 'my_attribute' => 'asc' },
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,84 @@
1
+ require 'rspectacular'
2
+ require 'apill/resource/processors/filtering'
3
+
4
+ module Apill
5
+ module Resource
6
+ module Processors
7
+ describe Filtering do
8
+ let(:filtering_resource) { double }
9
+
10
+ it 'can return the resource if not filtered parameters are passed in' do
11
+ filtering = Filtering.new(filtering_resource)
12
+
13
+ expect(filtering.processed).to eql filtering_resource
14
+ end
15
+
16
+ it 'can return a queried resource' do
17
+ filtering = Filtering.new(filtering_resource,
18
+ 'filter' => {
19
+ 'stuff' => 'blah',
20
+ })
21
+
22
+ allow(filtering_resource).to receive(:for_stuff).
23
+ with('blah').
24
+ and_return 'stuffed'
25
+
26
+ expect(filtering.processed).to eql 'stuffed'
27
+ end
28
+
29
+ it 'does not try to query if the resource cannot query for that thing' do
30
+ filtering = Filtering.new(filtering_resource,
31
+ 'filter' => {
32
+ 'whatever' => 'blah',
33
+ })
34
+
35
+ expect(filtering.processed).to eql filtering_resource
36
+ end
37
+
38
+ it 'can query for something that does not take arguments' do
39
+ filtering = Filtering.new(filtering_resource,
40
+ 'filter' => {
41
+ 'stuff' => 'blah',
42
+ })
43
+
44
+ allow(filtering_resource).to receive(:stuff).
45
+ and_return 'stuffed'
46
+
47
+ expect(filtering.processed).to eql 'stuffed'
48
+ end
49
+
50
+ it 'can query for something that does not take arguments' do
51
+ filtering = Filtering.new(filtering_resource,
52
+ 'filter' => {
53
+ 'stuff' => 'blah',
54
+ 'other_stuff' => 'other_blah',
55
+ })
56
+
57
+ allow(filtering_resource).to receive(:for_stuff).
58
+ with('blah').
59
+ and_return filtering_resource
60
+ allow(filtering_resource).to receive(:other_stuff).
61
+ and_return 'other_stuffed'
62
+
63
+ expect(filtering.processed).to eql 'other_stuffed'
64
+ end
65
+
66
+ it 'can handle objects (eg ActiveRelation) that store their proxy class in klass' do
67
+ resource_class = double
68
+ filtering = Filtering.new(filtering_resource,
69
+ 'filter' => {
70
+ 'stuff' => 'blah',
71
+ })
72
+
73
+ allow(filtering_resource).to receive(:klass).
74
+ and_return(resource_class)
75
+ allow(resource_class).to receive(:stuff)
76
+ allow(filtering_resource).to receive(:stuff).
77
+ and_return 'stuffed'
78
+
79
+ expect(filtering.processed).to eql 'stuffed'
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspectacular'
2
+ require 'apill/resource/processors/indexing'
3
+
4
+ module Apill
5
+ module Resource
6
+ module Processors
7
+ describe Indexing do
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,54 @@
1
+ require 'rspectacular'
2
+ require 'apill/resource/processors/paging'
3
+
4
+ module Apill
5
+ module Resource
6
+ module Processors
7
+ describe Paging do
8
+ let(:paging_resource) { double }
9
+ let(:processed_resource) { double }
10
+
11
+ it 'can return a default page' do
12
+ paging = Paging.new(paging_resource)
13
+
14
+ allow(processed_resource).to receive(:total_pages).and_return 10
15
+ allow(processed_resource).to receive(:current_page).and_return 1
16
+ allow(processed_resource).to receive(:prev_page).and_return nil
17
+ allow(processed_resource).to receive(:next_page).and_return nil
18
+
19
+ allow(paging_resource).to receive(:page).
20
+ with(1).
21
+ and_return paging_resource
22
+ allow(paging_resource).to receive(:per).
23
+ with(25).
24
+ and_return processed_resource
25
+
26
+ expect(paging.processed).to eql processed_resource
27
+ expect(paging.meta).to eql(
28
+ 'total-pages' => 10,
29
+ 'current-page' => 1,
30
+ 'previous-page' => nil,
31
+ 'next-page' => nil,
32
+ )
33
+ end
34
+
35
+ it 'can return a pageed resource' do
36
+ paging = Paging.new(paging_resource,
37
+ 'page' => {
38
+ 'number' => 5,
39
+ 'size' => 10,
40
+ })
41
+
42
+ allow(paging_resource).to receive(:page).
43
+ with(5).
44
+ and_return paging_resource
45
+ allow(paging_resource).to receive(:per).
46
+ with(10).
47
+ and_return processed_resource
48
+
49
+ expect(paging.processed).to eql processed_resource
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,74 @@
1
+ require 'rspectacular'
2
+ require 'apill/resource/processors/sorting'
3
+
4
+ module Apill
5
+ module Resource
6
+ module Processors
7
+ describe Sorting do
8
+ let(:sorting_resource) { double }
9
+
10
+ it 'can return a default sort' do
11
+ sorting = Sorting.new(sorting_resource)
12
+
13
+ allow(sorting_resource).to receive(:order).
14
+ with('created_at' => 'desc').
15
+ and_return('sorted')
16
+
17
+ expect(sorting.processed).to eql 'sorted'
18
+ expect(sorting.meta).to eql(
19
+ 'sort' => {
20
+ 'created_at' => 'desc',
21
+ },
22
+ )
23
+ end
24
+
25
+ it 'can return an ascending sort' do
26
+ sorting = Sorting.new(sorting_resource, 'sort' => 'my_attribute')
27
+
28
+ allow(sorting_resource).to receive(:order).
29
+ with('my_attribute' => 'asc').
30
+ and_return('sorted')
31
+
32
+ expect(sorting.processed).to eql 'sorted'
33
+ expect(sorting.meta).to eql(
34
+ 'sort' => {
35
+ 'my_attribute' => 'asc',
36
+ },
37
+ )
38
+ end
39
+
40
+ it 'can return a descending sort' do
41
+ sorting = Sorting.new(sorting_resource, 'sort' => '-my_attribute')
42
+
43
+ allow(sorting_resource).to receive(:order).
44
+ with('my_attribute' => 'desc').
45
+ and_return('sorted')
46
+
47
+ expect(sorting.processed).to eql 'sorted'
48
+ expect(sorting.meta).to eql(
49
+ 'sort' => {
50
+ 'my_attribute' => 'desc',
51
+ },
52
+ )
53
+ end
54
+
55
+ it 'can return multiple sorts' do
56
+ sorting = Sorting.new(sorting_resource, 'sort' => '-my_attribute,my_other_attribute')
57
+
58
+ allow(sorting_resource).to receive(:order).
59
+ with('my_attribute' => 'desc',
60
+ 'my_other_attribute' => 'asc').
61
+ and_return('sorted')
62
+
63
+ expect(sorting.processed).to eql 'sorted'
64
+ expect(sorting.meta).to eql(
65
+ 'sort' => {
66
+ 'my_attribute' => 'desc',
67
+ 'my_other_attribute' => 'asc',
68
+ },
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
74
+ 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: 2.9.0
4
+ version: 3.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: 2015-08-07 00:00:00.000000000 Z
11
+ date: 2015-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: human_error
@@ -16,28 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.0'
27
- - !ruby/object:Gem::Dependency
28
- name: kaminari
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 0.16.2
19
+ version: '3.0'
34
20
  type: :runtime
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: 0.16.2
26
+ version: '3.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -85,14 +71,20 @@ files:
85
71
  - lib/apill/matchers/subdomain_matcher.rb
86
72
  - lib/apill/matchers/version_matcher.rb
87
73
  - lib/apill/middleware/api_request.rb
88
- - lib/apill/mixins/indexable.rb
89
- - lib/apill/mixins/pageable.rb
90
- - lib/apill/mixins/queryable.rb
91
- - lib/apill/mixins/sortable.rb
92
74
  - lib/apill/parameters.rb
75
+ - lib/apill/parameters/filter.rb
76
+ - lib/apill/parameters/index.rb
77
+ - lib/apill/parameters/page.rb
78
+ - lib/apill/parameters/sort.rb
93
79
  - lib/apill/requests/base.rb
94
80
  - lib/apill/requests/rack_request.rb
95
81
  - lib/apill/requests/rails_request.rb
82
+ - lib/apill/resource.rb
83
+ - lib/apill/resource/model.rb
84
+ - lib/apill/resource/processors/filtering.rb
85
+ - lib/apill/resource/processors/indexing.rb
86
+ - lib/apill/resource/processors/paging.rb
87
+ - lib/apill/resource/processors/sorting.rb
96
88
  - lib/apill/responses/invalid_api_request_response.rb
97
89
  - lib/apill/responses/invalid_subdomain_response.rb
98
90
  - lib/apill/serializers/json_api.rb
@@ -108,6 +100,11 @@ files:
108
100
  - spec/apill/parameters_spec.rb
109
101
  - spec/apill/requests/rack_request_spec.rb
110
102
  - spec/apill/requests/rails_request_spec.rb
103
+ - spec/apill/resource/model_spec.rb
104
+ - spec/apill/resource/processors/filtering_spec.rb
105
+ - spec/apill/resource/processors/indexing_spec.rb
106
+ - spec/apill/resource/processors/paging_spec.rb
107
+ - spec/apill/resource/processors/sorting_spec.rb
111
108
  homepage: https://github.com/jfelchner/apill
112
109
  licenses:
113
110
  - MIT
@@ -144,3 +141,8 @@ test_files:
144
141
  - spec/apill/parameters_spec.rb
145
142
  - spec/apill/requests/rack_request_spec.rb
146
143
  - spec/apill/requests/rails_request_spec.rb
144
+ - spec/apill/resource/model_spec.rb
145
+ - spec/apill/resource/processors/filtering_spec.rb
146
+ - spec/apill/resource/processors/indexing_spec.rb
147
+ - spec/apill/resource/processors/paging_spec.rb
148
+ - spec/apill/resource/processors/sorting_spec.rb
@@ -1,49 +0,0 @@
1
- module Apill
2
- module Mixins
3
- module Indexable
4
- module ClassMethods
5
- def index(model)
6
- define_method(:indexed_model_name) do
7
- model
8
- end
9
- end
10
- end
11
-
12
- def self.included(base)
13
- base.extend ClassMethods
14
- end
15
-
16
- private
17
-
18
- def index_params
19
- @index_params ||= params[:index_params] || params || {}
20
- end
21
-
22
- def filtered_resource
23
- @filtered_resource ||= begin
24
- resource = if defined? super
25
- super
26
- else
27
- __send__(indexed_model_name)
28
- end
29
-
30
- if index_params.key? 'q'
31
- resource.search(index_params['q'])
32
- else
33
- resource
34
- end
35
- end
36
- end
37
-
38
- def filter_data
39
- filter_data = defined?(super) ? super : {}
40
-
41
- filter_data.merge(indexing_data)
42
- end
43
-
44
- def indexing_data
45
- {}
46
- end
47
- end
48
- end
49
- end
@@ -1,51 +0,0 @@
1
- module Apill
2
- module Mixins
3
- module Pageable
4
- module ClassMethods
5
- def paginate(model)
6
- define_method(:paginated_model_name) do
7
- model
8
- end
9
- end
10
- end
11
-
12
- def self.included(base)
13
- base.extend ClassMethods
14
- end
15
-
16
- private
17
-
18
- def filtered_resource
19
- @filtered_resource ||= begin
20
- page_number = params[:page]
21
- items_per_page = params[:per_page]
22
-
23
- resource = if defined? super
24
- super
25
- else
26
- __send__(paginated_model_name)
27
- end
28
-
29
- @paginated_resource = resource.
30
- page(page_number).
31
- per(items_per_page)
32
- end
33
- end
34
-
35
- def filter_data
36
- filter_data = defined?(super) ? super : {}
37
-
38
- filter_data.merge(pagination_data)
39
- end
40
-
41
- def pagination_data
42
- {
43
- 'total-pages' => @paginated_resource.total_pages,
44
- 'current-page' => @paginated_resource.current_page,
45
- 'previous-page' => @paginated_resource.prev_page,
46
- 'next-page' => @paginated_resource.next_page,
47
- }
48
- end
49
- end
50
- end
51
- end