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: f48730065ece9a8f8e18749b49d15d4f312c997148e86447f09ba8779bd5958d
4
- data.tar.gz: 5670802d6da87396caf59e961240dd2bb2d4dcc5b7bf5dafcc4ba83a196add72
3
+ metadata.gz: 0f59a0e96b77d27b0c274c7bfbd3d02d58bf10139079f7e8f65f0f141700657a
4
+ data.tar.gz: 48f2caa082efe138a25ce949383e04959b07f2cc6e383fc277059b19c7edb489
5
5
  SHA512:
6
- metadata.gz: 2aaf0603ed13870e685757e6f17d8e3e98db880bb2bf0c31785940b0333eb1720cd84c5e15eb54854e6dce2dceb3d5d8780b91ef0c361fa14a498701928ee72f
7
- data.tar.gz: af308e82aee39b175b46e19c2ecaafab1a7eb12bb87cd072d2fd33bce808863c003f2b9796cf82329ebd3301cfb20ffd7b4f83145be72f6546a4d707d678037e
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PackAPI
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
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.1
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