e_plat 0.5.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +109 -60
  3. data/lib/e_plat/client/default_request_args.rb +1 -1
  4. data/lib/e_plat/client.rb +55 -9
  5. data/lib/e_plat/initializer.rb +26 -0
  6. data/lib/e_plat/mapping/bigcommerce/v_3/product/option.rb +58 -0
  7. data/lib/e_plat/mapping/bigcommerce/v_3/product/variant/option_value.rb +42 -0
  8. data/lib/e_plat/mapping/shopify/v_2024_07/metafield.rb +26 -0
  9. data/lib/e_plat/mapping/shopify/v_2024_07/order/billing_address.rb +14 -0
  10. data/lib/e_plat/mapping/shopify/v_2024_07/order/shipping_address.rb +30 -0
  11. data/lib/e_plat/mapping/shopify/v_2024_07/order.rb +26 -0
  12. data/lib/e_plat/mapping/shopify/v_2024_07/product/image.rb +52 -0
  13. data/lib/e_plat/mapping/shopify/v_2024_07/product/option.rb +45 -0
  14. data/lib/e_plat/mapping/shopify/v_2024_07/product/variant/option_value.rb +58 -0
  15. data/lib/e_plat/mapping/shopify/v_2024_07/product/variant.rb +180 -0
  16. data/lib/e_plat/mapping/shopify/v_2024_07/product.rb +100 -0
  17. data/lib/e_plat/mapping/shopify/v_2024_07/script_tag.rb +26 -0
  18. data/lib/e_plat/mapping/shopify/v_2024_07/shop.rb +26 -0
  19. data/lib/e_plat/mapping/shopify/v_2024_07/webhook.rb +29 -0
  20. data/lib/e_plat/mapping/virtual_collection/shopify/product/variant/option_value.rb +272 -0
  21. data/lib/e_plat/mapping.rb +39 -15
  22. data/lib/e_plat/resource/base.rb +39 -5
  23. data/lib/e_plat/resource/collection.rb +45 -16
  24. data/lib/e_plat/resource/concerns/aliases.rb +39 -19
  25. data/lib/e_plat/resource/concerns/dirty.rb +12 -8
  26. data/lib/e_plat/resource/concerns/graph_q_lable.rb +253 -0
  27. data/lib/e_plat/resource/concerns/overwrite_instance_methods.rb +4 -4
  28. data/lib/e_plat/resource/concerns/overwrite_request_methods.rb +64 -21
  29. data/lib/e_plat/resource/countable.rb +37 -10
  30. data/lib/e_plat/resource/metafield.rb +2 -2
  31. data/lib/e_plat/resource/platform_specific/bigcommerce/metafield.rb +4 -0
  32. data/lib/e_plat/resource/platform_specific/bigcommerce/order/Consignment.rb +2 -0
  33. data/lib/e_plat/resource/platform_specific/bigcommerce/order/billing_address.rb +2 -0
  34. data/lib/e_plat/resource/platform_specific/bigcommerce/order/fulfillment.rb +2 -0
  35. data/lib/e_plat/resource/platform_specific/bigcommerce/order/line_item.rb +2 -0
  36. data/lib/e_plat/resource/platform_specific/bigcommerce/order/shipping_address.rb +2 -0
  37. data/lib/e_plat/resource/platform_specific/bigcommerce/order/shipping_line.rb +2 -0
  38. data/lib/e_plat/resource/platform_specific/bigcommerce/order.rb +4 -0
  39. data/lib/e_plat/resource/platform_specific/bigcommerce/product/image.rb +2 -0
  40. data/lib/e_plat/resource/platform_specific/bigcommerce/product/option.rb +2 -0
  41. data/lib/e_plat/resource/platform_specific/bigcommerce/product/variant/option_value.rb +2 -0
  42. data/lib/e_plat/resource/platform_specific/bigcommerce/product/variant.rb +2 -0
  43. data/lib/e_plat/resource/platform_specific/bigcommerce/product.rb +4 -0
  44. data/lib/e_plat/resource/platform_specific/bigcommerce/script_tag.rb +4 -0
  45. data/lib/e_plat/resource/platform_specific/bigcommerce/shop.rb +4 -0
  46. data/lib/e_plat/resource/platform_specific/bigcommerce/webhook.rb +4 -0
  47. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product/variant.rb +47 -0
  48. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product/variants_bulk.rb +47 -0
  49. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product.rb +23 -0
  50. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input.rb +98 -0
  51. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/mutation/product/variant.rb +78 -0
  52. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/mutation/product.rb +55 -0
  53. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/mutation.rb +5 -0
  54. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/query/product/variant.rb +44 -0
  55. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/query/product.rb +58 -0
  56. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/query.rb +5 -0
  57. data/lib/e_plat/resource/platform_specific/shopify/metafield.rb +4 -0
  58. data/lib/e_plat/resource/platform_specific/shopify/order/Consignment.rb +2 -0
  59. data/lib/e_plat/resource/platform_specific/shopify/order/billing_address.rb +2 -0
  60. data/lib/e_plat/resource/platform_specific/shopify/order/fulfillment.rb +2 -0
  61. data/lib/e_plat/resource/platform_specific/shopify/order/line_item.rb +2 -0
  62. data/lib/e_plat/resource/platform_specific/shopify/order/shipping_address.rb +2 -0
  63. data/lib/e_plat/resource/platform_specific/shopify/order/shipping_line.rb +2 -0
  64. data/lib/e_plat/resource/platform_specific/shopify/order.rb +4 -0
  65. data/lib/e_plat/resource/platform_specific/shopify/product/image.rb +14 -0
  66. data/lib/e_plat/resource/platform_specific/shopify/product/option.rb +14 -0
  67. data/lib/e_plat/resource/platform_specific/shopify/product/variant/option_value.rb +14 -0
  68. data/lib/e_plat/resource/platform_specific/shopify/product/variant.rb +66 -0
  69. data/lib/e_plat/resource/platform_specific/shopify/product.rb +79 -0
  70. data/lib/e_plat/resource/platform_specific/shopify/recurring_application_charge/usage_charge.rb +2 -0
  71. data/lib/e_plat/resource/platform_specific/shopify/recurring_application_charge.rb +4 -0
  72. data/lib/e_plat/resource/platform_specific/shopify/script_tag.rb +4 -0
  73. data/lib/e_plat/resource/platform_specific/shopify/shop.rb +4 -0
  74. data/lib/e_plat/resource/platform_specific/shopify/webhook.rb +4 -0
  75. data/lib/e_plat/resource/product/image.rb +8 -5
  76. data/lib/e_plat/resource/product/option.rb +2 -3
  77. data/lib/e_plat/resource/product/variant/option_value.rb +17 -0
  78. data/lib/e_plat/resource/product/variant.rb +6 -14
  79. data/lib/e_plat/resource/product.rb +23 -11
  80. data/lib/e_plat/session.rb +3 -2
  81. data/lib/e_plat/type_coercer.rb +4 -2
  82. data/lib/e_plat/version.rb +1 -1
  83. data/lib/e_plat.rb +33 -4
  84. data/lib/hash.rb +39 -0
  85. metadata +163 -27
@@ -0,0 +1,272 @@
1
+
2
+ module EPlat
3
+ class Mapping
4
+ module VirtualCollection
5
+ module Shopify
6
+ module Product
7
+ module Variant
8
+ class OptionValue < VirtualCollection::Base
9
+ attr_accessor :resource
10
+
11
+ def initialize(resource:, map_from:)
12
+ @resource = resource
13
+ super
14
+
15
+ return unless @resource.try :selected_options
16
+
17
+ @resource.option_values =
18
+ @resource.selected_options.map.with_index do |data, i|
19
+ item = EPlat::Product::Variant::OptionValue.new(variant_id: @resource.id)
20
+ item.mapping.virtual_collection = self
21
+ item.add_aliases!(native_attribute_aliases(i), EPlat::Product::Variant::OptionValue.schema)
22
+
23
+ item
24
+ end
25
+ end
26
+
27
+ def native_attribute_aliases(i)
28
+ [
29
+ {
30
+ alias_attribute: {
31
+ native_key: "[selected_options][#{i}][option_value][id]",
32
+ e_plat_key: "id",
33
+ virtual_collection: true,
34
+ custom_e_plat_getter: '-> { _1&.split("/")&.last }',
35
+ custom_native_setter: '-> { "gid://shopify/ProductOptionValue/#{_1}" }'
36
+ }
37
+ },
38
+ {
39
+ alias_attribute: {
40
+ native_key: "[selected_options][#{i}][name]",
41
+ e_plat_key: "name",
42
+ virtual_collection: true
43
+ }
44
+ },
45
+ {
46
+ alias_attribute: {
47
+ native_key: "[selected_options][#{i}][value]",
48
+ e_plat_key: "value",
49
+ virtual_collection: true
50
+ }
51
+ }
52
+ ]
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+
66
+ # consignments are deliveries, we're just going to operate within the context on one delivery per order
67
+ # this should be reasonable to tie us over for now
68
+
69
+ # integer :id
70
+ # string :admin_graphql_api_id
71
+ # integer :fulfillable_quantity
72
+ # string :fulfillment_service
73
+ # string :fulfillment_status
74
+ # boolean :gift_card
75
+ # integer :grams
76
+ # string :name
77
+ # string :price
78
+ # hash :price_set
79
+ # boolean :product_exists
80
+ # integer :product_id
81
+ # array :properties
82
+ # integer :quantity
83
+ # boolean :requires_shipping
84
+ # string :sku
85
+ # boolean :taxable
86
+ # string :title
87
+ # string :total_discount
88
+ # hash :total_discount_set
89
+ # integer :variant_id
90
+ # string :variant_inventory_management
91
+ # string :variant_title
92
+ # string :vendor
93
+ # array :tax_lines
94
+ # array :duties
95
+ # array :discount_allocations
96
+
97
+
98
+ # BG = {
99
+ # "consignments": [
100
+ # {
101
+ # "pickups": [],
102
+ # "shipping": [
103
+ # {
104
+ # "id": 3,
105
+ # "first_name": "Jane",
106
+ # "last_name": "Doe",
107
+ # "company": "",
108
+ # "street_1": "123 Main Street",
109
+ # "street_2": "",
110
+ # "city": "Austin",
111
+ # "zip": "78751",
112
+ # "country": "United States",
113
+ # "country_iso2": "US",
114
+ # "state": "Texas",
115
+ # "email": "oli@example.com",
116
+ # "phone": "",
117
+ # "form_fields": [],
118
+ # "line_items": [
119
+ # {
120
+ # "id": 3,
121
+ # "order_id": 102,
122
+ # "product_id": 88,
123
+ # "variant_id": 81,
124
+ # "order_pickup_method_id": 0,
125
+ # "order_address_id": 3,
126
+ # "name": "Chemex Coffeemaker",
127
+ # "name_customer": "Chemex Coffeemaker",
128
+ # "name_merchant": "Chemex Coffeemaker",
129
+ # "sku": "CC3C-SM",
130
+ # "upc": "",
131
+ # "type": "physical",
132
+ # "base_price": "83.3300",
133
+ # "price_ex_tax": "83.3300",
134
+ # "price_inc_tax": "83.3300",
135
+ # "price_tax": "0.0000",
136
+ # "base_total": "83.3300",
137
+ # "total_ex_tax": "83.3300",
138
+ # "total_inc_tax": "83.3300",
139
+ # "total_tax": "0.0000",
140
+ # "weight": "1.0000",
141
+ # "width": "0.0000",
142
+ # "height": "0.0000",
143
+ # "depth": "0.0000",
144
+ # "quantity": 1,
145
+ # "base_cost_price": "0.0000",
146
+ # "cost_price_inc_tax": "0.0000",
147
+ # "cost_price_ex_tax": "0.0000",
148
+ # "cost_price_tax": "0.0000",
149
+ # "is_refunded": false,
150
+ # "quantity_refunded": 0,
151
+ # "refund_amount": "0.0000",
152
+ # "return_id": 0,
153
+ # "wrapping_id": 0,
154
+ # "wrapping_name": "",
155
+ # "base_wrapping_cost": "0.0000",
156
+ # "wrapping_cost_ex_tax": "0.0000",
157
+ # "wrapping_cost_inc_tax": "0.0000",
158
+ # "wrapping_cost_tax": "0.0000",
159
+ # "wrapping_message": "",
160
+ # "quantity_shipped": 0,
161
+ # "event_name": null,
162
+ # "event_date": null,
163
+ # "fixed_shipping_cost": "0.0000",
164
+ # "ebay_item_id": "",
165
+ # "ebay_transaction_id": "",
166
+ # "option_set_id": 18,
167
+ # "parent_order_product_id": null,
168
+ # "is_bundled_product": false,
169
+ # "bin_picking_number": "0",
170
+ # "external_id": "",
171
+ # "fulfillment_source": "",
172
+ # "brand": "",
173
+ # "gift_certificate_id": null,
174
+ # "applied_discounts": [],
175
+ # "product_options": [
176
+ # {
177
+ # "id": 2,
178
+ # "option_id": 34,
179
+ # "order_product_id": 3,
180
+ # "product_option_id": 114,
181
+ # "display_name": "Size",
182
+ # "display_name_customer": "Size",
183
+ # "display_name_merchant": "Size",
184
+ # "display_value": "Small",
185
+ # "display_value_customer": "Small",
186
+ # "display_value_merchant": "Small",
187
+ # "value": "101",
188
+ # "type": "Multiple choice",
189
+ # "name": "Size1699441692-88",
190
+ # "display_style": "Dropdown"
191
+ # }
192
+ # ],
193
+ # "configurable_fields": [],
194
+ # "discounted_total_inc_tax": "83.3300"
195
+ # }
196
+ # ]
197
+ # }
198
+ # ],
199
+ # "items_total": 1,
200
+ # "items_shipped": 0,
201
+ # "shipping_method": "None",
202
+ # "base_cost": 0,
203
+ # "cost_ex_tax": 0,
204
+ # "cost_inc_tax": 0,
205
+ # "cost_tax": 0,
206
+ # "cost_tax_class_id": 0,
207
+ # "base_handling_cost": 0,
208
+ # "handling_cost_ex_tax": 0,
209
+ # "handling_cost_inc_tax": 0,
210
+ # "handling_cost_tax": 0,
211
+ # "handling_cost_tax_class_id": 0,
212
+ # "shipping_zone_id": 0,
213
+ # "shipping_zone_name": "",
214
+ # "shipping_quotes": {
215
+ # "url": "https://api.bigcommerce.com/stores/d0kmq3bory/v2/orders/102/consignments/shipping/3/shipping_quotes",
216
+ # "resource": "/orders/102/consignments/shipping/3/shipping_quotes"
217
+ # }
218
+ # }
219
+ # ]
220
+ # }
221
+
222
+ # Shopify = {
223
+ # "line_items": [
224
+ # {
225
+ # "id": 13120677806211,
226
+ # "admin_graphql_api_id": "gid://shopify/LineItem/13120677806211",
227
+ # "fulfillable_quantity": 1,
228
+ # "fulfillment_service": "manual",
229
+ # "fulfillment_status": null,
230
+ # "gift_card": false,
231
+ # "grams": 0,
232
+ # "name": "Winter Luggage - Sienna the longest name variant in the world",
233
+ # "price": "150.00",
234
+ # "price_set": {
235
+ # "shop_money": {
236
+ # "amount": "150.00",
237
+ # "currency_code": "GBP"
238
+ # },
239
+ # "presentment_money": {
240
+ # "amount": "150.00",
241
+ # "currency_code": "GBP"
242
+ # }
243
+ # },
244
+ # "product_exists": true,
245
+ # "product_id": 6610509627523,
246
+ # "properties": [],
247
+ # "quantity": 1,
248
+ # "requires_shipping": true,
249
+ # "sku": "skusku123",
250
+ # "taxable": true,
251
+ # "title": "Winter Luggage",
252
+ # "total_discount": "0.00",
253
+ # "total_discount_set": {
254
+ # "shop_money": {
255
+ # "amount": "0.00",
256
+ # "currency_code": "GBP"
257
+ # },
258
+ # "presentment_money": {
259
+ # "amount": "0.00",
260
+ # "currency_code": "GBP"
261
+ # }
262
+ # },
263
+ # "variant_id": 39428269408387,
264
+ # "variant_inventory_management": "shopify",
265
+ # "variant_title": "Sienna the longest name variant in the world",
266
+ # "vendor": "PreProduct Test",
267
+ # "tax_lines": [],
268
+ # "duties": [],
269
+ # "discount_allocations": []
270
+ # }
271
+ # ]
272
+ # }
@@ -1,19 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EPlat
4
- class Mapping
5
-
6
- class << self
7
-
8
- def new_instance(specifc_mapping:, resource: nil)
9
- if Object.const_defined?(specifc_mapping)
10
- mapping = specifc_mapping.constantize.new(resource)
11
- else
12
- mapping = EPlat::Mapping::Base.new(resource)
13
- end
14
- end
15
-
16
- end
17
-
18
- end
4
+ class Mapping
5
+
6
+ class << self
7
+
8
+ def new_instance(specifc_mapping:, resource: nil)
9
+ platform = EPlat::Current.e_plat_session.platform.to_s
10
+ sanitized_mapping = sanitize_mapping(specifc_mapping, platform)
11
+
12
+ if Object.const_defined?(sanitized_mapping)
13
+ sanitized_mapping.constantize.new(resource)
14
+ elsif Object.const_defined?(specifc_mapping)
15
+ specifc_mapping.constantize.new(resource)
16
+ else
17
+ EPlat::Mapping::Base.new(resource)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def sanitize_mapping(specifc_mapping, platform)
24
+ # Stripe out all instanced of the platform scope in the mapping
25
+ # then put platform back in as third scope, as mapping is always platform scoped.
26
+ # i.e. EPlat::Mapping::Bigcommerce::V3::Product::Variant
27
+ parts = specifc_mapping.split("::")
28
+
29
+ parts.reject! do |part|
30
+ platform.downcase == part.downcase
31
+ end
32
+
33
+ parts.insert(2, platform.capitalize)
34
+
35
+ parts.join("::")
36
+ end
37
+
38
+
39
+ end
40
+
41
+ end
19
42
  end
43
+
@@ -4,11 +4,12 @@ module EPlat
4
4
  class Base < ActiveResource::Base
5
5
  include Concerns::OverwriteRequestMethods, Concerns::OverwriteInstanceMethods
6
6
  include Concerns::Aliases, Concerns::Dirty, Concerns::FullJson
7
+ include Concerns::GraphQLable
7
8
 
8
9
  self.connection_class = EPlat::Connection
9
10
  self.collection_parser = EPlat::Collection
10
11
  self.site = "/" # this is overwritten below in initialize_singleton! when EPlat::Session is initialized
11
- add_response_method :full_response
12
+ add_response_method :full_response #full response of the request
12
13
 
13
14
  class << self
14
15
  include Countable
@@ -18,6 +19,12 @@ module EPlat
18
19
  threadsafe_attribute :_headers, :_connection, :_user, :_password, :_bearer_token, :_site, :_proxy, # active resource
19
20
  :mapping, :include_root_in_json, :include_format_in_path # e_plat specific
20
21
 
22
+ def inherited(subclass)
23
+ super
24
+ subclass.exclude_from_json(read_only) if read_only
25
+ subclass.schema ||= schema.dup
26
+ end
27
+
21
28
  def initialize_singleton!
22
29
  self.site = client.base_url # .site is an ActiveResource method that uses the threadsafe _site attribute underneath
23
30
  self.prefix = client.url_prefix(klass: self) # this is overwritten below in prefix= for threadsafety
@@ -60,6 +67,9 @@ module EPlat
60
67
  self.read_only = read_only
61
68
  end
62
69
 
70
+ def platform_specific_class?
71
+ self.class.to_s.include? client.platform.to_s.capitalize
72
+ end
63
73
 
64
74
  private
65
75
 
@@ -83,6 +93,7 @@ module EPlat
83
93
 
84
94
  @mapped_attributes = ensure_e_plat_attributes!
85
95
  ensure_native_attributes!
96
+
86
97
  add_aliases!(mapping.native_attribute_aliases, type_schema || {})
87
98
  end
88
99
 
@@ -96,7 +107,7 @@ module EPlat
96
107
 
97
108
  def native_keys
98
109
  if client.shopify?
99
- schema.keys # EPlat uses Shopify's schema, so keys are always native
110
+ @mapping.native_attributes.presence || schema.keys # EPlat uses Shopify's schema, apar from a couple of Graphql only fields
100
111
  elsif mapping.class.to_s != EPlat::Mapping::Base && @mapping.native_attributes.present?
101
112
  @mapping.native_attributes.presence
102
113
  else
@@ -117,7 +128,26 @@ module EPlat
117
128
 
118
129
  top_level_resource?
119
130
  end
120
-
131
+
132
+ def formatted_id(id)
133
+ return id unless client.shopify? && client.api_version != "2024_01"
134
+ return if id.to_s.include?("gid://")
135
+
136
+ case element_name
137
+ when "product"
138
+ "gid://shopify/Product/#{id}"
139
+ when "variant"
140
+ "gid://shopify/ProductVariant/#{id}"
141
+ when "image"
142
+ "gid://shopify/ProductImage/#{id}"
143
+ else
144
+ raise EPlat::Errors.new("Unsupported element name")
145
+ end
146
+ end
147
+
148
+ def graphql_input
149
+ raise EPlat::Error.new("GraphQL input not supported for this resource and platform")
150
+ end
121
151
 
122
152
  private
123
153
 
@@ -126,7 +156,7 @@ module EPlat
126
156
  end
127
157
 
128
158
  def ensure_native_attributes!
129
- return if client.shopify? # EPlat uses Shopify's schema
159
+ return if client.shopify? && EPlat.config.shopify_api_version == "2024_01"
130
160
 
131
161
  AttributeInterface.new(self, keys: native_keys)
132
162
  end
@@ -170,7 +200,11 @@ module EPlat
170
200
  end
171
201
 
172
202
  def top_level_resource?
173
- self.class.name.split("::").count <= 2
203
+ parts = self.class.name.split("::")
204
+ # platform scope not included, as top level resources can optionally be platform specific
205
+ parts.reject!{|part| EPlat::SUPPORTED_PLATFORMS.include? part.downcase }
206
+
207
+ parts.count <= 2
174
208
  end
175
209
 
176
210
  def element_name
@@ -2,41 +2,52 @@
2
2
 
3
3
  module EPlat
4
4
  class Collection < ActiveResource::Collection
5
+ SHOPIFY_GRAPHQL_PAGINATABLE_CLASSES = %w(
6
+ EPlat::Product
7
+ EPlat::Product::Variant
8
+ EPlat::Shopify::Product
9
+ EPlat::Shopify::Product::Variant
10
+ )
5
11
 
6
12
  def initialize(parsed = {})
7
13
  super parsed
8
14
  end
9
15
 
10
16
  def next_page?
11
- return unless pagination_available?
12
- @next_url.present?
17
+ return false unless pagination_available?
18
+ @next.present?
13
19
  end
14
20
 
15
21
  def previous_page?
16
- return unless pagination_available?
17
- @previous_url.present?
22
+ return false unless pagination_available?
23
+ @previous.present?
18
24
  end
19
25
 
20
26
  def next_page_info
21
27
  return unless pagination_available?
22
- extract_page_info(@next_url)
28
+ extract_page_info(@next)
23
29
  end
24
30
 
25
31
  def previous_page_info
26
32
  return unless pagination_available?
27
- extract_page_info(@previous_url)
33
+ extract_page_info(@previous)
28
34
  end
29
35
 
30
36
  def fetch_next_page
31
37
  return unless pagination_available?
32
- fetch_page(@next_url)
38
+ fetch_page(@next)
33
39
  end
34
40
 
35
41
  def fetch_previous_page
36
42
  return unless pagination_available?
37
- fetch_page(@previous_url)
43
+ fetch_page(@previous)
38
44
  end
45
+
46
+ def paginates_via_graphql?
47
+ return unless SHOPIFY_GRAPHQL_PAGINATABLE_CLASSES.include? resource_class.to_s
39
48
 
49
+ client.shopify? && client.api_version != "2024_01"
50
+ end
40
51
 
41
52
  private
42
53
 
@@ -44,10 +55,17 @@ module EPlat
44
55
  return [] unless url.present?
45
56
  url = "#{ resource_class.collection_path }#{ url }" if client.bigcommerce?
46
57
 
47
- resource_class.all(from: url)
58
+ if paginates_via_graphql?
59
+ arg_name = (url == @next) ? "after" : "before"
60
+ resource_class.find(:all, params: {arg_name => url})
61
+ else
62
+ resource_class.all(from: url)
63
+ end
48
64
  end
49
65
 
50
66
  def pagination_links
67
+ return pagination_cursors if paginates_via_graphql?
68
+
51
69
  case client.platform
52
70
  when :shopify
53
71
  @pagination_links ||= EPlat::Paginated::LinkHeaders.new(
@@ -67,17 +85,28 @@ module EPlat
67
85
  end
68
86
  end
69
87
 
70
- def pagination_available?
71
- @next_url = pagination_links&.next_link&.url&.to_s
72
- @previous_url = pagination_links&.previous_link&.url&.to_s
88
+ def pagination_cursors
89
+ response_body = JSON.parse(full_response.body)
90
+ page_info = response_body.dig("data", resource_class.graphql_collection_name, "pageInfo") || {}
91
+
92
+ earliest_cursor = page_info["hasPreviousPage"] ? page_info["startCursor"] : nil
93
+ latest_cursor = page_info["hasNextPage"] ? page_info["endCursor"] : nil
73
94
 
74
- @next_url.present? or @previous_url.present?
95
+ [earliest_cursor, latest_cursor]
96
+ end
97
+
98
+ def pagination_available?
99
+ @previous = (paginates_via_graphql?) ? pagination_cursors[0] : pagination_links&.previous_link&.url&.to_s
100
+ @next = (paginates_via_graphql?) ? pagination_cursors[1] : pagination_links&.next_link&.url&.to_s
101
+
102
+ @next.present? or @previous.present?
75
103
  end
76
104
 
77
- def extract_page_info(url)
78
- return unless url.present?
105
+ def extract_page_info(request_data)
106
+ return unless request_data.present?
107
+ return request_data if paginates_via_graphql?
79
108
 
80
- query_params = URI.decode_www_form(URI(url).query).to_h
109
+ query_params = URI.decode_www_form(URI(request_data).query).to_h
81
110
  query_params[client.pagination_param]
82
111
  end
83
112
 
@@ -8,33 +8,35 @@ module EPlat
8
8
 
9
9
  def add_aliases!(aliases, type_schema)
10
10
  @type_coercer = EPlat::TypeCoercer.new(type_schema)
11
- processed = []
11
+ processed = { getter: [], setter: [] }
12
12
 
13
13
  aliases.each do |action|
14
- action_name = action.keys.first
15
- args = action.values.first
16
- e_plat_key = args[:e_plat_key]
17
- native_key = args[:native_key]
18
- @is_virtual = args[:virtual_collection]
14
+ action_name, args = action.keys.first, action.values.first
15
+ e_plat_key, native_key = args[:e_plat_key], args[:native_key]
16
+ @is_virtual = !!args[:virtual_collection]
19
17
 
20
18
  case action_name
21
19
  when :alias_attribute
22
20
  add_to_instance! alias_mapped_getter(e_plat_key, native_key, proc: args[:custom_e_plat_getter])
23
21
  add_to_instance! alias_mapped_setter(e_plat_key, native_key, proc: args[:custom_native_setter])
24
- # puts alias_mapped_setter(e_plat_key, native_key, proc: args[:custom_native_setter]) if e_plat_key == 'variant_id'
25
- processed << e_plat_key
22
+ processed[:getter] << e_plat_key
23
+ processed[:setter] << e_plat_key
24
+ when :existing_entry
25
+ add_to_instance! mapped_getter(e_plat_key, proc: args[:custom_e_plat_getter])
26
+ add_to_instance! native_setter(native_key, proc: args[:custom_native_setter])
27
+ processed[:getter] << e_plat_key
28
+ processed[:setter] << native_key
26
29
  end
27
30
  end
28
31
 
29
32
  type_schema.each do |e_plat_key, type|
30
- next if processed.include?(e_plat_key)
33
+ next if processed[:getter].include?(e_plat_key)
31
34
  add_to_instance! mapped_getter(e_plat_key), @is_virtual
32
35
  end
33
36
 
34
37
  native_keys.each do |native_key|
35
- next if processed.include?(native_key)
36
-
37
- add_to_instance! native_setter(native_key), @is_virtual
38
+ add_to_instance!(mapped_getter(native_key), @is_virtual) unless processed[:getter].include?(native_key)
39
+ add_to_instance!(native_setter(native_key), @is_virtual) unless processed[:setter].include?(native_key)
38
40
  end
39
41
  end
40
42
 
@@ -42,7 +44,7 @@ module EPlat
42
44
  private
43
45
 
44
46
 
45
- def alias_mapped_getter(e_plat_key, native_key, proc: nil)
47
+ def alias_mapped_getter(e_plat_key, native_key, proc: '->{_1}')
46
48
  proc_line = (proc) ? "current_value = #{ proc.strip }.call(current_value)" : nil
47
49
 
48
50
  <<-STRING
@@ -54,7 +56,7 @@ module EPlat
54
56
  STRING
55
57
  end
56
58
 
57
- def alias_mapped_setter(e_plat_key, native_key, proc: nil)
59
+ def alias_mapped_setter(e_plat_key, native_key, proc: '->{_1}')
58
60
  proc_line = (proc) ? "new_value = #{ proc.strip }.call(new_value)" : nil
59
61
 
60
62
  <<-STRING
@@ -71,19 +73,28 @@ module EPlat
71
73
  STRING
72
74
  end
73
75
 
74
- def mapped_getter(e_plat_key)
76
+ def mapped_getter(e_plat_key, proc: '->{_1}')
77
+ proc_line = (proc) ? "value = #{ proc.strip }.call(value)" : nil
78
+
75
79
  <<-STRING
76
80
  def #{ e_plat_key }
77
- type_coercer.type_value!('#{ e_plat_key }', self.attributes['#{ e_plat_key }'])
81
+ value = self.attributes['#{ e_plat_key }']
82
+ #{ proc_line }
83
+ type_coercer.type_value!('#{ e_plat_key }', value)
78
84
  end
79
85
  STRING
80
86
  end
81
87
 
82
- def native_setter(native_key)
88
+ # we force the native_setter result into changed_attributes, incase the key is the same as the e_plat key
89
+ # we dont want the eplat version of the value in change_attributes, we want the native version.
90
+ def native_setter(native_key, proc: '->{_1}')
91
+ proc_line = (proc) ? "value = #{ proc.strip }.call(value)" : nil
92
+
83
93
  <<-STRING
84
94
  def #{native_key}=(value)
95
+ #{proc_line}
85
96
  super
86
- attribute_will_change!('#{native_key}')
97
+ attribute_will_change!('#{native_key}', force_value: value)
87
98
  end
88
99
  STRING
89
100
  end
@@ -136,8 +147,17 @@ module EPlat
136
147
  change_statements << change_statement
137
148
  end
138
149
 
150
+ method_chain_array = method_chain.split(".").compact_blank
151
+ will_change_statement =
152
+ if method_chain_array.one? or @is_virtual
153
+ "attribute_will_change!('#{method_chain_array.last}')"
154
+ elsif method_chain_array.many?
155
+ method_chain_array[-1] = "attribute_will_change!('#{method_chain_array.last}')"
156
+ "#{method_chain_array.join('.')}"
157
+ end
158
+
139
159
  <<-STRING
140
- attribute_will_change!('#{parts.last}')
160
+ #{ will_change_statement }
141
161
  #{change_statements.join("\n")}
142
162
  STRING
143
163
  end