apill 2.9.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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