pack_api 1.0.1 → 1.0.2
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
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f59a0e96b77d27b0c274c7bfbd3d02d58bf10139079f7e8f65f0f141700657a
|
|
4
|
+
data.tar.gz: 48f2caa082efe138a25ce949383e04959b07f2cc6e383fc277059b19c7edb489
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b81ed5a8dd8536b534c18774efbdd21eb3d8799a9b00aa96afa015d5fed12ff626e48c5035983a7b28760f6a0a40c06b205d55bc2e93f82c67b29381360ec27
|
|
7
|
+
data.tar.gz: 7a7bda196011de7a10e1a8c8b98961c530dee455bc4d908f1d0c9b5c726c7df47bdeef29906a9f8faa0a65a169dfdfbadb8e1eb8c0cbb15dff35b3086c3abd42
|
data/README.md
CHANGED
|
@@ -228,6 +228,55 @@ def query_blog_posts(cursor = nil, search = nil, sort = nil, page_size = 50, fil
|
|
|
228
228
|
end
|
|
229
229
|
```
|
|
230
230
|
|
|
231
|
+
## Testing with Shared Examples
|
|
232
|
+
|
|
233
|
+
PackAPI includes RSpec shared examples to help test your API query methods. These are opt-in and only need to be loaded if you're using RSpec.
|
|
234
|
+
|
|
235
|
+
### Loading Shared Examples
|
|
236
|
+
|
|
237
|
+
In your `spec_helper.rb` or `rails_helper.rb`, require the shared examples you need:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
# Load all shared examples
|
|
241
|
+
require 'pack_api/rspec/shared_examples_for_api_query_methods'
|
|
242
|
+
require 'pack_api/rspec/shared_examples_for_paginated_results'
|
|
243
|
+
|
|
244
|
+
# Or load them individually as needed
|
|
245
|
+
require 'pack_api/rspec/shared_examples_for_api_query_methods'
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Using the Shared Examples
|
|
249
|
+
|
|
250
|
+
**Testing API Query Methods:**
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
RSpec.describe 'query_blog_posts' do
|
|
254
|
+
let(:api_query_method) { method(:query_blog_posts) }
|
|
255
|
+
let(:resources) { BlogPost.all }
|
|
256
|
+
|
|
257
|
+
it_behaves_like 'an API query method'
|
|
258
|
+
|
|
259
|
+
# With custom options
|
|
260
|
+
it_behaves_like 'an API query method',
|
|
261
|
+
model_id_attribute: :uuid,
|
|
262
|
+
supports_search: true do
|
|
263
|
+
let(:search_terms) { "searchable text" }
|
|
264
|
+
let(:matched_resources) { BlogPost.where("title LIKE ?", "%searchable%") }
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Testing Paginated Methods:**
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
RSpec.describe 'paginated query' do
|
|
273
|
+
let(:paginated_api_query_method) { method(:query_blog_posts) }
|
|
274
|
+
let(:paginated_resources) { BlogPost.all }
|
|
275
|
+
|
|
276
|
+
it_behaves_like 'a paginated API method', model_id_attribute: :external_id
|
|
277
|
+
end
|
|
278
|
+
```
|
|
279
|
+
|
|
231
280
|
## Development
|
|
232
281
|
|
|
233
282
|
After checking out the repo, run:
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PackAPI
|
|
4
|
+
###
|
|
5
|
+
# Assumes the following variables are defined:
|
|
6
|
+
# - api_query_method: the method to call
|
|
7
|
+
# - resources: the resources to compare against (must be more than 1)
|
|
8
|
+
#
|
|
9
|
+
# If the options contain a key :model_id_attribute, then the public id will be mapped to the given model attribute.
|
|
10
|
+
# Otherwise, it defaults to :external_id.
|
|
11
|
+
#
|
|
12
|
+
# If the options contain a key: `supports_search` set to `true`, then
|
|
13
|
+
# the following variables are also required:
|
|
14
|
+
# - search_terms: a string, representing the search terms to use
|
|
15
|
+
# - matched_resources: *at least 2* resources that will be returned by the search
|
|
16
|
+
RSpec.shared_examples 'an API query method' do |**options|
|
|
17
|
+
let(:model_id_attribute) { options[:model_id_attribute] || :external_id }
|
|
18
|
+
|
|
19
|
+
context 'with no id' do
|
|
20
|
+
it 'returns a successful result with all resources' do
|
|
21
|
+
# when
|
|
22
|
+
result = api_query_method.call
|
|
23
|
+
|
|
24
|
+
# then
|
|
25
|
+
expect(result.success).to be_truthy, -> { result.errors }
|
|
26
|
+
expect(result.errors).to be_nil
|
|
27
|
+
expect(result.value).not_to be_nil
|
|
28
|
+
expect(result.value).to have(resources.count).items
|
|
29
|
+
expect(result.value.pluck(:id).sort).to match_array(resources.pluck(model_id_attribute).sort)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'with nil id' do
|
|
34
|
+
it 'returns a successful result with all resources' do
|
|
35
|
+
# when
|
|
36
|
+
result = api_query_method.call(id: nil)
|
|
37
|
+
|
|
38
|
+
# then
|
|
39
|
+
expect(result.success).to be_truthy, -> { result.errors }
|
|
40
|
+
expect(result.errors).to be_nil
|
|
41
|
+
expect(result.value).not_to be_nil
|
|
42
|
+
expect(result.value).to have(resources.count).items
|
|
43
|
+
expect(result.value.pluck(:id).sort).to match_array(resources.pluck(model_id_attribute).sort)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'with an empty string id' do
|
|
48
|
+
it 'returns a successful result with no resources' do
|
|
49
|
+
# when
|
|
50
|
+
result = api_query_method.call(id: '')
|
|
51
|
+
|
|
52
|
+
# then
|
|
53
|
+
expect(result.success).to be_truthy, -> { result.errors }
|
|
54
|
+
expect(result.errors).to be_nil
|
|
55
|
+
expect(result.value).not_to be_nil
|
|
56
|
+
expect(result.value).to have(0).items
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context 'with an empty array id' do
|
|
61
|
+
it 'returns a successful result with no resources' do
|
|
62
|
+
# when
|
|
63
|
+
result = api_query_method.call(id: [])
|
|
64
|
+
|
|
65
|
+
# then
|
|
66
|
+
expect(result.success).to be_truthy, -> { result.errors }
|
|
67
|
+
expect(result.errors).to be_nil
|
|
68
|
+
expect(result.value).not_to be_nil
|
|
69
|
+
expect(result.value).to have(0).items
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context 'with an unknown id' do
|
|
74
|
+
it 'returns an empty success result' do
|
|
75
|
+
result = api_query_method.call(id: 'unknown')
|
|
76
|
+
expect(result.success).to be_truthy, -> { result.errors }
|
|
77
|
+
expect(result.value).to have(0).items
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'with a known id' do
|
|
82
|
+
it 'returns a success result with the resource' do
|
|
83
|
+
value = api_query_method.call(id: resources.first[model_id_attribute])
|
|
84
|
+
expect(value.success).to be_truthy, -> { result.errors }
|
|
85
|
+
expect(value.value).to have(1).item
|
|
86
|
+
expect(value.value.first.id).to eq(resources.first[model_id_attribute])
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it_behaves_like 'a paginated API method', model_id_attribute: options[:model_id_attribute] || :external_id do
|
|
91
|
+
let(:paginated_api_query_method) { api_query_method }
|
|
92
|
+
let(:paginated_resources) { resources }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
context 'with search terms', if: options[:supports_search] do
|
|
96
|
+
it 'limits results to matching resources' do
|
|
97
|
+
# when
|
|
98
|
+
result = api_query_method.call(search_terms:)
|
|
99
|
+
# then
|
|
100
|
+
expect(result.success).to be_truthy, -> { result.errors }
|
|
101
|
+
expect(result.value).to have(matched_resources.size).item
|
|
102
|
+
matched_resource_ids = matched_resources.pluck(model_id_attribute)
|
|
103
|
+
result.value.each do |resource|
|
|
104
|
+
expect(matched_resource_ids).to include(resource.id)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it_behaves_like 'a paginated API method', model_id_attribute: options[:model_id_attribute] || :external_id do
|
|
109
|
+
let(:paginated_api_query_method) do
|
|
110
|
+
->(**args) { api_query_method.call(search_terms:, **args) }
|
|
111
|
+
end
|
|
112
|
+
let(:paginated_resources) { matched_resources }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'supports sorting by API attributes only' do
|
|
117
|
+
resources.each_with_index { |resource, index| resource.update(model_id_attribute => format('%02d', index + 1)) }
|
|
118
|
+
# when - sort by API attribute `id` in descending order
|
|
119
|
+
results = api_query_method.call(sort: { id: :desc })
|
|
120
|
+
# then - results should be in model_id_attribute in descending order
|
|
121
|
+
expect(results.success).to be_truthy, -> { results.errors }
|
|
122
|
+
expect(results.value.first.id).to eq(resources.last[model_id_attribute])
|
|
123
|
+
expect(results.value.last.id).to eq(resources.first[model_id_attribute])
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PackAPI
|
|
4
|
+
###
|
|
5
|
+
# Assumes the following variables are defined:
|
|
6
|
+
# - paginated_api_query_method: the method to call
|
|
7
|
+
# - paginated_resources: the resources to compare against (must be more than 1)
|
|
8
|
+
#
|
|
9
|
+
# If the options contain a key :model_id_attribute, then the public id will be mapped to the given model attribute.
|
|
10
|
+
# Otherwise, it defaults to :external_id.
|
|
11
|
+
# If the options contain a key :public_id_attribute, that will be used to access the resource identifier in the results.
|
|
12
|
+
RSpec.shared_examples 'a paginated API method' do |**options|
|
|
13
|
+
let(:public_id_attribute) { options[:public_id_attribute] || :id }
|
|
14
|
+
let(:model_id_attribute) { options[:model_id_attribute] || :external_id }
|
|
15
|
+
|
|
16
|
+
it 'can access the resources page-by-page' do
|
|
17
|
+
returned_object_ids = []
|
|
18
|
+
|
|
19
|
+
page_one_results = paginated_api_query_method.call(per_page: 1)
|
|
20
|
+
expect(page_one_results.success).to be_truthy, -> { page_one_results.errors }
|
|
21
|
+
expect(page_one_results.value).to have(1).item
|
|
22
|
+
expect(page_one_results.collection_metadata.next_page_cursor).not_to be_nil
|
|
23
|
+
returned_object_ids << page_one_results.value.first.send(public_id_attribute)
|
|
24
|
+
next_page_cursor = page_one_results.collection_metadata.next_page_cursor
|
|
25
|
+
|
|
26
|
+
(paginated_resources.count - 1).times do |index|
|
|
27
|
+
next_page_results = paginated_api_query_method.call(per_page: 1, cursor: next_page_cursor)
|
|
28
|
+
expect(next_page_results.success).to be_truthy, -> { next_page_results.errors }
|
|
29
|
+
expect(next_page_results.value).to have(1).item
|
|
30
|
+
if index < paginated_resources.count - 2 # if not last page
|
|
31
|
+
expect(next_page_results.collection_metadata.next_page_cursor).not_to be_nil, 'next page cursor should not be nil'
|
|
32
|
+
next_page_cursor = next_page_results.collection_metadata.next_page_cursor
|
|
33
|
+
else
|
|
34
|
+
expect(next_page_results.collection_metadata.next_page_cursor).to be_nil, 'next page cursor should be nil'
|
|
35
|
+
end
|
|
36
|
+
returned_object_ids << next_page_results.value.first.send(public_id_attribute)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# verify all objects were returned
|
|
40
|
+
expect(returned_object_ids.sort).to match_array(paginated_resources.pluck(model_id_attribute).sort)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'can access the metadata (without data)' do
|
|
44
|
+
results = paginated_api_query_method.call(per_page: 0)
|
|
45
|
+
expect(results.success).to be_truthy, -> { results.errors }
|
|
46
|
+
expect(results.value).to be_empty
|
|
47
|
+
expect(results.collection_metadata).not_to be_nil
|
|
48
|
+
expect(results.collection_metadata.total_items).to eq(paginated_resources.count)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/pack_api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pack_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Flytedesk
|
|
@@ -230,6 +230,8 @@ files:
|
|
|
230
230
|
- lib/pack_api/querying/dynamic_enum_filter.rb
|
|
231
231
|
- lib/pack_api/querying/filter_factory.rb
|
|
232
232
|
- lib/pack_api/querying/sort_hash.rb
|
|
233
|
+
- lib/pack_api/rspec/shared_examples_for_api_query_methods.rb
|
|
234
|
+
- lib/pack_api/rspec/shared_examples_for_paginated_results.rb
|
|
233
235
|
- lib/pack_api/types/aggregate_type.rb
|
|
234
236
|
- lib/pack_api/types/base_type.rb
|
|
235
237
|
- lib/pack_api/types/boolean_filter_definition.rb
|