pack_api 1.0.1 → 1.0.3
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/README.md +49 -0
- data/lib/pack_api/frozen_empty.rb +4 -0
- data/lib/pack_api/mapping/abstract_transformer.rb +4 -2
- data/lib/pack_api/mapping/api_to_model_attributes_transformer.rb +2 -9
- data/lib/pack_api/mapping/attribute_hash_transformer.rb +0 -1
- data/lib/pack_api/mapping/attribute_map.rb +18 -9
- data/lib/pack_api/mapping/filter_map.rb +2 -5
- data/lib/pack_api/mapping/model_to_api_attributes_transformer.rb +8 -11
- data/lib/pack_api/mapping/value_object_factory.rb +0 -1
- data/lib/pack_api/querying/dynamic_enum_filter.rb +1 -3
- data/lib/pack_api/rspec/shared_examples_for_api_query_methods.rb +126 -0
- data/lib/pack_api/rspec/shared_examples_for_paginated_results.rb +51 -0
- data/lib/pack_api/version.rb +1 -1
- data/lib/pack_api.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 246f379e82f7226306211a320a9f7ec9d7f9d251be0f2817c815a071b8159422
|
|
4
|
+
data.tar.gz: 939d5be377373d26a4e62ba7fb2870c595aadb52c7527f01c89c512d8af77856
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58ac5c88da900f602058aa151a38193bbda938baafd761380c1257079b7ded5e9b107c065dc251b4bc66e39e87fd1f6f623222798c9f50e3cb4dae251a02e6b2
|
|
7
|
+
data.tar.gz: eaf29a74517055cf5c97596da942d0d2f42197791b53931a2b59d67130e2afa2a6b0db8efa11a24d7c938279e2e111728440aabdef08e9a9282370ba390a001b
|
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:
|
|
@@ -10,7 +10,7 @@ module PackAPI::Mapping
|
|
|
10
10
|
@api_type = config[:api_type]
|
|
11
11
|
@model_type = config[:model_type]
|
|
12
12
|
@transform_value = config[:transform_value]
|
|
13
|
-
@options =
|
|
13
|
+
@options = PackAPI::FrozenEmpty::HASH
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
###
|
|
@@ -20,7 +20,7 @@ module PackAPI::Mapping
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def options=(value)
|
|
23
|
-
@options = value.presence ||
|
|
23
|
+
@options = value.presence || PackAPI::FrozenEmpty::HASH
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
protected
|
|
@@ -30,8 +30,10 @@ module PackAPI::Mapping
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def model_attribute(api_attribute)
|
|
33
|
+
# support _destroy and other special attributes used by Rails
|
|
33
34
|
return api_attribute if api_attribute.start_with?('_')
|
|
34
35
|
|
|
36
|
+
# prevent api_attributes from being used unless they are in the public type definition
|
|
35
37
|
unless mappings.key?(api_attribute)
|
|
36
38
|
raise ActiveModel::UnknownAttributeError.new(@model_type.name, api_attribute)
|
|
37
39
|
end
|
|
@@ -5,23 +5,16 @@ module PackAPI::Mapping
|
|
|
5
5
|
# Specialized attribute transformer allowing API attributes be converted to the attribute names needed to
|
|
6
6
|
# creating/updating an ActiveRecord model.
|
|
7
7
|
class APIToModelAttributesTransformer < AbstractTransformer
|
|
8
|
-
|
|
9
8
|
def execute
|
|
10
9
|
result = {}
|
|
11
10
|
attribute_names = NormalizedAPIAttribute.new(api_attribute_names)
|
|
12
11
|
data_source.each do |api_attribute, api_value|
|
|
13
12
|
normalized_api_attribute = attribute_names.normalize(api_attribute)
|
|
14
13
|
model_attribute = model_attribute(normalized_api_attribute)
|
|
15
|
-
model_value =
|
|
16
|
-
result
|
|
14
|
+
model_value = transform_value(normalized_api_attribute, api_value)
|
|
15
|
+
result[model_attribute] = model_value
|
|
17
16
|
end
|
|
18
17
|
result
|
|
19
18
|
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def model_value(api_attribute, api_value)
|
|
24
|
-
transform_value(api_attribute, api_value)
|
|
25
|
-
end
|
|
26
19
|
end
|
|
27
20
|
end
|
|
@@ -9,7 +9,6 @@ module PackAPI::Mapping
|
|
|
9
9
|
# Converts model attributes to API attributes
|
|
10
10
|
# Converts API attributes to model attributes
|
|
11
11
|
class AttributeHashTransformer < AbstractTransformer
|
|
12
|
-
|
|
13
12
|
def execute
|
|
14
13
|
options.fetch(:contains_model_attributes, true) ?
|
|
15
14
|
model_attributes_to_api_attributes :
|
|
@@ -26,8 +26,6 @@
|
|
|
26
26
|
# associate the error with the "user_id" attribute (not the "user" attribute).
|
|
27
27
|
module PackAPI::Mapping
|
|
28
28
|
class AttributeMap
|
|
29
|
-
FROZEN_EMPTY_HASH = {}.freeze
|
|
30
|
-
|
|
31
29
|
attr_reader :config, :options
|
|
32
30
|
|
|
33
31
|
class << self
|
|
@@ -225,25 +223,36 @@ module PackAPI::Mapping
|
|
|
225
223
|
end
|
|
226
224
|
|
|
227
225
|
def call(attribute_map, attribute_value)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
if @kwargs
|
|
227
|
+
proc ?
|
|
228
|
+
attribute_map.instance_exec(attribute_value, **@kwargs, &proc) :
|
|
229
|
+
attribute_map.send(instance_method.name, attribute_value, **@kwargs)
|
|
230
|
+
else
|
|
231
|
+
proc ?
|
|
232
|
+
attribute_map.instance_exec(attribute_value, &proc) :
|
|
233
|
+
attribute_map.send(instance_method.name, attribute_value)
|
|
234
|
+
end
|
|
231
235
|
end
|
|
232
236
|
|
|
233
237
|
def kwargs=(new_kwargs)
|
|
234
|
-
@kwargs =
|
|
238
|
+
@kwargs = new_kwargs == DEFAULT_OPTIONS || new_kwargs.blank? ?
|
|
239
|
+
nil :
|
|
240
|
+
supported_kwargs(new_kwargs)
|
|
235
241
|
end
|
|
236
242
|
|
|
237
243
|
private
|
|
238
244
|
|
|
239
245
|
def supported_kwargs(kwargs)
|
|
240
|
-
return
|
|
246
|
+
return if parameters.empty?
|
|
247
|
+
|
|
248
|
+
overlap = parameters.any? { |p| kwargs.key?(p) }
|
|
249
|
+
return unless overlap
|
|
241
250
|
|
|
242
|
-
kwargs.
|
|
251
|
+
kwargs.slice(*parameters)
|
|
243
252
|
end
|
|
244
253
|
|
|
245
254
|
def parameters
|
|
246
|
-
@parameters ||= (proc || instance_method).parameters
|
|
255
|
+
@parameters ||= (proc || instance_method).parameters.map(&:last)
|
|
247
256
|
end
|
|
248
257
|
end
|
|
249
258
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PackAPI::Mapping
|
|
4
|
-
FROZEN_EMPTY_ARRAY = [].freeze
|
|
5
|
-
FROZEN_EMPTY_HASH = {}.freeze
|
|
6
|
-
|
|
7
4
|
##
|
|
8
5
|
# This class is responsible for transforming API filter names into model filters. It also produces filter definitions
|
|
9
6
|
# in API terms for those filters that are supported by the model.
|
|
@@ -43,7 +40,7 @@ module PackAPI::Mapping
|
|
|
43
40
|
|
|
44
41
|
def api_attribute_filter_names
|
|
45
42
|
@api_attribute_filter_names ||= attribute_map_class.nil? ?
|
|
46
|
-
|
|
43
|
+
PackAPI::FrozenEmpty::ARRAY :
|
|
47
44
|
attribute_map_class.api_type.filterable_attributes.keys
|
|
48
45
|
end
|
|
49
46
|
|
|
@@ -51,7 +48,7 @@ module PackAPI::Mapping
|
|
|
51
48
|
# Map from a backend filter name to an API filter name for default attribute filters.
|
|
52
49
|
def api_attribute_filter_name_map
|
|
53
50
|
return @api_attribute_filter_name_map if defined?(@api_attribute_filter_name_map)
|
|
54
|
-
return
|
|
51
|
+
return PackAPI::FrozenEmpty::HASH if attribute_map_class.nil?
|
|
55
52
|
|
|
56
53
|
names = {}
|
|
57
54
|
api_attribute_filter_names.each { |name| names[name] = name }
|
|
@@ -5,7 +5,6 @@ module PackAPI::Mapping
|
|
|
5
5
|
# Specialized attribute transformer converting an ActiveRecord model attributes to the attribute names needed
|
|
6
6
|
# to create a ValueObject in the public API.
|
|
7
7
|
class ModelToAPIAttributesTransformer < AbstractTransformer
|
|
8
|
-
|
|
9
8
|
def options=(options)
|
|
10
9
|
super
|
|
11
10
|
@optional_attributes_to_include = nil
|
|
@@ -17,15 +16,17 @@ module PackAPI::Mapping
|
|
|
17
16
|
model_attribute = model_attribute(api_attribute)
|
|
18
17
|
next unless include_model_attribute?(model_attribute)
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
end
|
|
23
|
-
result[api_attribute] = value
|
|
19
|
+
api_value = transform_value(api_attribute, model_value(model_attribute)) if include_api_attribute?(api_attribute)
|
|
20
|
+
result[api_attribute] = api_value
|
|
24
21
|
end
|
|
25
22
|
end
|
|
26
23
|
|
|
27
24
|
protected
|
|
28
25
|
|
|
26
|
+
def include_api_attribute?(api_attribute)
|
|
27
|
+
!optional_api_attribute?(api_attribute) || include_optional_api_attribute?(api_attribute)
|
|
28
|
+
end
|
|
29
|
+
|
|
29
30
|
def model_attributes_of_interest
|
|
30
31
|
@model_attributes_of_interest ||= options[:model_attributes_of_interest]
|
|
31
32
|
end
|
|
@@ -38,7 +39,7 @@ module PackAPI::Mapping
|
|
|
38
39
|
api_type_optional_attributes.include?(api_attribute_name)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
def
|
|
42
|
+
def include_optional_api_attribute?(api_attribute_name)
|
|
42
43
|
return false if optional_attributes_to_include.nil?
|
|
43
44
|
return false if optional_attributes_to_include.respond_to?(:exclude?) &&
|
|
44
45
|
optional_attributes_to_include.exclude?(api_attribute_name)
|
|
@@ -53,15 +54,11 @@ module PackAPI::Mapping
|
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def api_type_optional_attributes
|
|
56
|
-
@api_type_optional_attributes ||= api_type.optional_attributes ||
|
|
57
|
+
@api_type_optional_attributes ||= api_type.optional_attributes || PackAPI::FrozenEmpty::ARRAY
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def model_value(model_attribute)
|
|
60
61
|
data_source.public_send(model_attribute)
|
|
61
62
|
end
|
|
62
|
-
|
|
63
|
-
def api_value(api_attribute, model_value)
|
|
64
|
-
transform_value(api_attribute, model_value)
|
|
65
|
-
end
|
|
66
63
|
end
|
|
67
64
|
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module PackAPI::Querying
|
|
4
4
|
module DynamicEnumFilter
|
|
5
|
-
FROZEN_EMPTY_ARRAY = [].freeze
|
|
6
|
-
|
|
7
5
|
extend ActiveSupport::Concern
|
|
8
6
|
class_methods do
|
|
9
7
|
def type = :dynamic_enum
|
|
@@ -14,7 +12,7 @@ module PackAPI::Querying
|
|
|
14
12
|
|
|
15
13
|
private
|
|
16
14
|
|
|
17
|
-
def filter_options(**) =
|
|
15
|
+
def filter_options(**) = PackAPI::FrozenEmpty::ARRAY
|
|
18
16
|
end
|
|
19
17
|
end
|
|
20
18
|
end
|
|
@@ -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
data/lib/pack_api.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative "types"
|
|
|
9
9
|
require_relative "pack_api/version"
|
|
10
10
|
|
|
11
11
|
module PackAPI
|
|
12
|
+
autoload :FrozenEmpty, "pack_api/frozen_empty"
|
|
12
13
|
autoload :InternalError, "pack_api/internal_error"
|
|
13
14
|
autoload :ValuesInBatches, "pack_api/values_in_batches"
|
|
14
15
|
autoload :ValuesInBackgroundBatches, "pack_api/values_in_background_batches"
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Flytedesk
|
|
@@ -199,6 +199,7 @@ files:
|
|
|
199
199
|
- LICENSE.txt
|
|
200
200
|
- README.md
|
|
201
201
|
- lib/pack_api.rb
|
|
202
|
+
- lib/pack_api/frozen_empty.rb
|
|
202
203
|
- lib/pack_api/internal_error.rb
|
|
203
204
|
- lib/pack_api/mapping/abstract_transformer.rb
|
|
204
205
|
- lib/pack_api/mapping/api_to_model_attributes_transformer.rb
|
|
@@ -230,6 +231,8 @@ files:
|
|
|
230
231
|
- lib/pack_api/querying/dynamic_enum_filter.rb
|
|
231
232
|
- lib/pack_api/querying/filter_factory.rb
|
|
232
233
|
- lib/pack_api/querying/sort_hash.rb
|
|
234
|
+
- lib/pack_api/rspec/shared_examples_for_api_query_methods.rb
|
|
235
|
+
- lib/pack_api/rspec/shared_examples_for_paginated_results.rb
|
|
233
236
|
- lib/pack_api/types/aggregate_type.rb
|
|
234
237
|
- lib/pack_api/types/base_type.rb
|
|
235
238
|
- lib/pack_api/types/boolean_filter_definition.rb
|