e_plat 0.3.0 → 0.4.0

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +630 -131
  3. data/lib/active_resource/connection_error.rb +29 -0
  4. data/lib/active_resource/formats.rb +27 -0
  5. data/lib/current.rb +1 -1
  6. data/lib/e_plat/client/default_request_args.rb +27 -3
  7. data/lib/e_plat/client.rb +114 -87
  8. data/lib/e_plat/connection.rb +4 -0
  9. data/lib/e_plat/mapping/base.rb +119 -12
  10. data/lib/e_plat/mapping/bigcommerce/v_3/metafield.rb +62 -0
  11. data/lib/e_plat/mapping/bigcommerce/v_3/order/billing_address.rb +14 -0
  12. data/lib/e_plat/mapping/bigcommerce/v_3/order/line_item.rb +85 -0
  13. data/lib/e_plat/mapping/bigcommerce/v_3/order/shipping_address.rb +73 -0
  14. data/lib/e_plat/mapping/bigcommerce/v_3/order.rb +160 -0
  15. data/lib/e_plat/mapping/bigcommerce/v_3/product/image.rb +12 -12
  16. data/lib/e_plat/mapping/bigcommerce/v_3/product/variant.rb +1 -1
  17. data/lib/e_plat/mapping/bigcommerce/v_3/script_tag.rb +78 -0
  18. data/lib/e_plat/mapping/bigcommerce/v_3/shop.rb +11 -6
  19. data/lib/e_plat/mapping/bigcommerce/v_3/webhook.rb +54 -0
  20. data/lib/e_plat/mapping/request_body_root.rb +38 -0
  21. data/lib/e_plat/mapping/shopify/v_2024_01/metafield.rb +26 -0
  22. data/lib/e_plat/mapping/shopify/v_2024_01/order/billing_address.rb +14 -0
  23. data/lib/e_plat/mapping/shopify/v_2024_01/order/shipping_address.rb +30 -0
  24. data/lib/e_plat/mapping/shopify/{v_2022_07/product.rb → v_2024_01/order.rb} +3 -3
  25. data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/product/image.rb +1 -1
  26. data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/product/variant.rb +2 -1
  27. data/lib/e_plat/mapping/shopify/v_2024_01/product.rb +26 -0
  28. data/lib/e_plat/mapping/shopify/v_2024_01/script_tag.rb +26 -0
  29. data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/shop.rb +1 -1
  30. data/lib/e_plat/mapping/shopify/v_2024_01/webhook.rb +29 -0
  31. data/lib/e_plat/mapping/virtual_collection/base.rb +14 -0
  32. data/lib/e_plat/mapping/virtual_collection/bigcommerce/order_line_items.rb +297 -0
  33. data/lib/e_plat/mapping.rb +3 -3
  34. data/lib/e_plat/resource/attribute_interface.rb +28 -28
  35. data/lib/e_plat/resource/base.rb +107 -66
  36. data/lib/e_plat/resource/collection.rb +92 -0
  37. data/lib/e_plat/resource/concerns/aliases.rb +102 -18
  38. data/lib/e_plat/resource/concerns/dirty.rb +54 -0
  39. data/lib/e_plat/resource/concerns/metafieldable.rb +43 -0
  40. data/lib/e_plat/resource/concerns/overwrite_instance_methods.rb +108 -6
  41. data/lib/e_plat/resource/concerns/overwrite_request_methods.rb +73 -37
  42. data/lib/e_plat/resource/countable.rb +43 -0
  43. data/lib/e_plat/resource/metafield.rb +70 -0
  44. data/lib/e_plat/resource/order/Consignment.rb +8 -0
  45. data/lib/e_plat/resource/order/billing_address.rb +6 -0
  46. data/lib/e_plat/resource/order/fulfillment.rb +1 -0
  47. data/lib/e_plat/resource/order/line_item.rb +1 -0
  48. data/lib/e_plat/resource/order/shipping_address.rb +44 -0
  49. data/lib/e_plat/resource/order/shipping_line.rb +15 -14
  50. data/lib/e_plat/resource/order.rb +32 -0
  51. data/lib/e_plat/resource/paginated/link_headers.rb +42 -0
  52. data/lib/e_plat/resource/paginated/link_params.rb +26 -0
  53. data/lib/e_plat/resource/product/image.rb +6 -0
  54. data/lib/e_plat/resource/product/option.rb +1 -0
  55. data/lib/e_plat/resource/product/variant.rb +8 -12
  56. data/lib/e_plat/resource/product.rb +8 -2
  57. data/lib/e_plat/resource/script_tag.rb +56 -0
  58. data/lib/e_plat/resource/shop.rb +17 -13
  59. data/lib/e_plat/resource/shopify_only/recurring_application_charge/usage_charge.rb +32 -0
  60. data/lib/e_plat/resource/shopify_only/recurring_application_charge.rb +47 -0
  61. data/lib/e_plat/resource/webhook.rb +50 -0
  62. data/lib/e_plat/session.rb +10 -7
  63. data/lib/e_plat/type_coercer.rb +15 -17
  64. data/lib/e_plat/version.rb +1 -1
  65. data/lib/e_plat.rb +4 -3
  66. metadata +55 -9
  67. data/lib/e_plat/resource/order/customer.rb +0 -37
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EPlat
4
+ class Collection < ActiveResource::Collection
5
+
6
+ def initialize(parsed = {})
7
+ super parsed
8
+ end
9
+
10
+ def next_page?
11
+ return unless pagination_available?
12
+ @next_url.present?
13
+ end
14
+
15
+ def previous_page?
16
+ return unless pagination_available?
17
+ @previous_url.present?
18
+ end
19
+
20
+ def next_page_info
21
+ return unless pagination_available?
22
+ extract_page_info(@next_url)
23
+ end
24
+
25
+ def previous_page_info
26
+ return unless pagination_available?
27
+ extract_page_info(@previous_url)
28
+ end
29
+
30
+ def fetch_next_page
31
+ return unless pagination_available?
32
+ fetch_page(@next_url)
33
+ end
34
+
35
+ def fetch_previous_page
36
+ return unless pagination_available?
37
+ fetch_page(@previous_url)
38
+ end
39
+
40
+
41
+ private
42
+
43
+ def fetch_page(url)
44
+ return [] unless url.present?
45
+ url = "#{ resource_class.collection_path }#{ url }" if client.bigcommerce?
46
+
47
+ resource_class.all(from: url)
48
+ end
49
+
50
+ def pagination_links
51
+ case client.platform
52
+ when :shopify
53
+ @pagination_links ||= EPlat::Paginated::LinkHeaders.new(
54
+ (full_response&.headers&.to_h || {})[:link]
55
+ )
56
+ when :bigcommerce
57
+ response = JSON.parse(full_response.body)
58
+ return unless response.is_a? Hash
59
+ link_params = response.dig('meta', 'pagination', 'links')
60
+ return unless link_params.present?
61
+
62
+ @pagination_links ||= EPlat::Paginated::LinkParams.new(
63
+ link_params
64
+ )
65
+ else
66
+ raise "Unknown platform"
67
+ end
68
+ end
69
+
70
+ def pagination_available?
71
+ @next_url = pagination_links&.next_link&.url&.to_s
72
+ @previous_url = pagination_links&.previous_link&.url&.to_s
73
+
74
+ @next_url.present? or @previous_url.present?
75
+ end
76
+
77
+ def extract_page_info(url)
78
+ return unless url.present?
79
+
80
+ query_params = URI.decode_www_form(URI(url).query).to_h
81
+ query_params[client.pagination_param]
82
+ end
83
+
84
+
85
+ private
86
+
87
+ def client
88
+ Current.e_plat_session
89
+ end
90
+
91
+ end
92
+ end
@@ -3,6 +3,7 @@ module EPlat
3
3
  module Concerns
4
4
  module Aliases
5
5
  extend ActiveSupport::Concern
6
+
6
7
  attr_accessor :type_coercer
7
8
 
8
9
  def add_aliases!(aliases, type_schema)
@@ -14,20 +15,27 @@ module EPlat
14
15
  args = action.values.first
15
16
  e_plat_key = args[:e_plat_key]
16
17
  native_key = args[:native_key]
17
-
18
+ @is_virtual = args[:virtual_collection]
19
+
18
20
  case action_name
19
21
  when :alias_attribute
20
22
  add_to_instance! alias_mapped_getter(e_plat_key, native_key, proc: args[:custom_e_plat_getter])
21
23
  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'
22
25
  processed << e_plat_key
23
26
  end
24
27
  end
25
28
 
26
29
  type_schema.each do |e_plat_key, type|
27
30
  next if processed.include?(e_plat_key)
28
- add_to_instance! mapped_getter(e_plat_key)
31
+ add_to_instance! mapped_getter(e_plat_key), @is_virtual
29
32
  end
30
33
 
34
+ 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
+ end
31
39
  end
32
40
 
33
41
 
@@ -39,7 +47,7 @@ module EPlat
39
47
 
40
48
  <<-STRING
41
49
  def #{ e_plat_key }
42
- current_value = self.attributes['#{ native_key }']
50
+ current_value = #{ native_key_path(native_key) }
43
51
  #{ proc_line }
44
52
  type_coercer.type_value!('#{ e_plat_key }', current_value)
45
53
  end
@@ -50,10 +58,15 @@ module EPlat
50
58
  proc_line = (proc) ? "new_value = #{ proc.strip }.call(new_value)" : nil
51
59
 
52
60
  <<-STRING
53
- def #{ e_plat_key }=(value)
61
+ def #{e_plat_key}=(value)
54
62
  new_value = value
55
63
  #{proc_line}
56
- self.attributes['#{ native_key }'] = new_value
64
+
65
+ unless new_value == #{ native_key_path(native_key) }
66
+ #{ native_key_path(native_key) } = new_value
67
+ attribute_will_change!('#{e_plat_key}')
68
+ #{ nested_attribute_will_change(native_key) if native_key&.include?('[') }
69
+ end
57
70
  end
58
71
  STRING
59
72
  end
@@ -66,11 +79,77 @@ module EPlat
66
79
  STRING
67
80
  end
68
81
 
69
- def add_to_instance!(meta_programming_string)
82
+ def native_setter(native_key)
83
+ <<-STRING
84
+ def #{native_key}=(value)
85
+ super
86
+ attribute_will_change!('#{native_key}')
87
+ end
88
+ STRING
89
+ end
90
+
91
+ def native_key_path(native_key)
92
+ segments = native_key.split(/[\[\]]+/).reject(&:empty?)
93
+ path_code = @is_virtual ? "self.mapping.virtual_collection.resource" : "self"
94
+
95
+ segments.each do |segment|
96
+ if segment.match(/^\d+$/) # Check if the segment is a number (array index)
97
+ path_code += "[#{segment}]"
98
+ else
99
+ path_code += ".#{segment}"
100
+ end
101
+ end
102
+
103
+ path_code
104
+ end
105
+
106
+
107
+ # ".send('#{ native_key_parent }').attribute_will_change!('#{ native_key }')"
108
+
109
+ def add_to_instance!(meta_programming_string, is_virtual=false)
110
+ # puts meta_programming_string if meta_programming_string.include?('note')
111
+
70
112
  self.instance_eval { eval meta_programming_string }
71
113
  end
72
-
73
-
114
+
115
+ # doesn't support two indexes in a row. i.e. [0][0]
116
+ def nested_attribute_will_change(native_key) # "[consignments][0][shipping][0][line_items][0][variant_id]"
117
+ ref_object = @is_virtual ? "mapping.virtual_collection.resource" : "self"
118
+ parts = native_key.scan(/(\w+)|(\d+)/).flatten.compact # ["consignments", "0", "shipping", "0", "line_items", "0", "variant_id"]
119
+
120
+ method_chain = ""
121
+ change_statements = []
122
+
123
+ first_attribute = parts.find { |part| !is_index?(part) }
124
+ if first_attribute
125
+ change_statements << "#{ref_object}.attribute_will_change!('#{first_attribute}')"
126
+ end
127
+
128
+ parts.each_with_index do |part, index|
129
+ method_chain += is_index?(part) ? "[#{part}]" : ".#{part}"
130
+
131
+ next_call = parts[index + 1]
132
+ attribute_key = is_index?(next_call) ? parts[index + 2] : next_call
133
+ next if (is_index?(part) && next_call && attribute_key.nil?) or !is_index?(part)
134
+
135
+ change_statement = "#{ref_object}#{method_chain}.attribute_will_change!('#{attribute_key}')"
136
+ change_statements << change_statement
137
+ end
138
+
139
+ <<-STRING
140
+ attribute_will_change!('#{parts.last}')
141
+ #{change_statements.join("\n")}
142
+ STRING
143
+ end
144
+
145
+
146
+ def is_index?(part)
147
+ part&.match?(/\d+/)
148
+ end
149
+
150
+
151
+
152
+
74
153
 
75
154
  # Requires an array of hashes which each contain a single :alias_attribute entry.
76
155
  # This will expose e_plat_key getter and setter methods that interact with the
@@ -82,19 +161,24 @@ module EPlat
82
161
  # Like the below example, optional stringified procs can be passed in to respect the e_plat or native interfaces version of the value. e.g. enums
83
162
  #
84
163
  # {
85
- # alias_attribute: {native_key: "name", e_plat_key: "handle"}
164
+ # alias_attribute: {native_key: "description", e_plat_key: "body_html"}
165
+ # }, {
166
+ # alias_attribute: {
167
+ # native_key: "description",
168
+ # e_plat_key: "body_html",
169
+ # custom_e_plat_getter: "-> (value) { value&.gsub(' ', '-') }",
170
+ # custom_native_setter: "-> (value) { value&.gsub('-', ' ') }"
171
+ # }
172
+ # }, {
173
+ # existing_entry: {native_key: "id", e_plat_key: "id"}
86
174
  # },
87
175
  # {
88
- # alias_attribute: {native_key: "availability", e_plat_key: "status",
89
- # custom_e_plat_getter: "-> (value) {
90
- # {'preorder'=>'active', 'available' => 'active', 'disabled' => 'draft'}[value] || value
91
- # }",
92
- # custom_native_setter: "-> (value) {
93
- # {'available' => 'active', 'disabled' => 'draft'}.invert[value] || value
94
- # }" #invert will merge new duped keys
95
- # }
176
+ # alias_attribute: {native_key: "billing_address[email]", e_plat_key: "email"}
96
177
  # }
97
-
178
+ # {
179
+ # alias_attribute: {native_key: "[nested][deeply][in][2][attribute]", e_plat_key: "hey"}
180
+ # }
181
+ #
98
182
  end
99
183
  end
100
184
  end
@@ -0,0 +1,54 @@
1
+ module EPlat
2
+ module Concerns
3
+ module Dirty
4
+ extend ActiveSupport::Concern
5
+ # manages the self.changed_attributes hash
6
+ # this is important for the as_json method, which will only include changed attributes when updating an existing resource
7
+ # only supports native attributes
8
+
9
+ included do
10
+ def changed_attributes
11
+ @changed_attributes ||= {}
12
+ end
13
+
14
+ def attribute_will_change!(attr)
15
+ changed_attributes[attr] = read_attribute(attr)
16
+
17
+ native_keys.include?(attr) ? e_plat_attribute_will_change!(attr) : native_attribute_will_change!(attr)
18
+ end
19
+
20
+ def attribute_changed?(attr)
21
+ changed_attributes.include?(attr)
22
+ end
23
+
24
+ def read_attribute(attr)
25
+ self.send(attr)
26
+ end
27
+
28
+ def reload!
29
+ try(:super)
30
+ changed_attributes.clear
31
+ end
32
+
33
+
34
+ private
35
+
36
+ def native_attribute_will_change!(eplat_attr)
37
+ key = self.mapping.native_key_to(eplat_attr)
38
+ return unless key && self.respond_to?(key)
39
+
40
+ changed_attributes[key] = send(key)
41
+ end
42
+
43
+ def e_plat_attribute_will_change!(native_attr)
44
+ key = self.mapping.e_plat_key_to(native_attr)
45
+ return unless key && self.respond_to?(key)
46
+
47
+ changed_attributes[key] = send(key)
48
+ end
49
+ end
50
+
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EPlat
4
+ module Concerns
5
+ module Metafieldable #< ActiveResource::CustomMethods
6
+
7
+ def metafields(**options)
8
+ options.merge!(resource: self.class.collection_name, resource_id: id)
9
+
10
+ Metafield.find(:all,
11
+ from: current_resources_metafield_path,
12
+ params: options
13
+ )
14
+ end
15
+
16
+ def find_metafield(id)
17
+ metafields.find { |m| m.id == id }
18
+ end
19
+
20
+ def add_metafield(metafield)
21
+ raise ArgumentError, "You can only add metafields to a resource that has been saved" if new?
22
+
23
+ metafield.owner_id = id
24
+ metafield.owner_resource = self.class.element_name
25
+ metafield.save
26
+ metafield
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def current_resources_metafield_path
33
+ uri = URI.parse(element_path)
34
+ uri.path = uri.path.gsub(".json", "/metafields.json")
35
+ uri.path += "/metafields" if uri.path.exclude?("json")
36
+ uri.path.gsub!('v2', 'v3') if client.bigcommerce? # metafield uses v3 API, even though orders are only available in v2
37
+
38
+ uri.to_s
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -1,11 +1,113 @@
1
1
  module EPlat
2
2
  module Concerns
3
- module OverwriteInstanceMethods
4
-
5
- def include_root_in_json
6
- client.shopify? && top_level_resource?
3
+ module OverwriteInstanceMethods
4
+ extend ActiveSupport::Concern
5
+
6
+ def to_json(options = {})
7
+ #add this option here so root is added at the top of the JSON, as_json to every nested resource
8
+ root_at_top_of_json = self.mapping.include_root_in_request_body?(self)
9
+ options[:root] = self.element_name if root_at_top_of_json
10
+
11
+ super(options)
7
12
  end
8
-
13
+
14
+ def as_json(options = {})
15
+ full_json = super(options)
16
+ return {} if read_only?
17
+
18
+ if mapping.class.virtual_collections.present?
19
+ virtual_keys = mapping.class.virtual_collections.map{|c| c[:name]}.map(&:to_s)
20
+
21
+ full_json.reject!{|key, value| virtual_keys.include?(key) } # not needed in JSON, as it's just a proxy for setting the real attributes
22
+ end
23
+
24
+ return full_json if new_record? # new records request with all attributes
25
+
26
+ request_json =
27
+ if include_root_in_json or options[:root].present?
28
+ { element_name => select_changed_attributes_and_collection_keys(full_json[element_name]) }
29
+ else
30
+ select_changed_attributes_and_collection_keys(full_json)
31
+ end
32
+
33
+ if top_level_resource? # then check that any present collections have all entries represented with atleast an ID, so they're not removed
34
+ hash_without_root(request_json).each do |key, value|
35
+ next unless arrayOfPresentHashes?(value)
36
+
37
+ hash_without_root(request_json)[key].filter!(&:present?)
38
+ hash_without_root(request_json)[key] += unrepresented_collection_ids(key, value).map{ |id| { "id" => id } }
39
+ end
40
+ end
41
+
42
+ add_root_if_needed(request_json, options[:root]).reject do |k,v|
43
+ if v.is_a?(Array)
44
+ v.reject(&:empty?).empty?
45
+ else
46
+ v.nil? || v.blank?
47
+ end
48
+ end
49
+ end
50
+
51
+ # Create and return a class definition for a resource inside the current resource
52
+ def create_resource_for(resource_name)
53
+ resource = Class.new(EPlat::Base) # <- this line changed
54
+ resource.prefix = self.class.prefix
55
+ resource.site = self.class.site
56
+ self.class.const_set(resource_name, resource)
57
+
58
+ resource
59
+ end
60
+
61
+ def create
62
+ self.attributes = mapping.via_native_attributes_where_possible(attributes)
63
+ super
64
+ end
65
+
66
+ def update
67
+ self.attributes = mapping.via_native_attributes_where_possible(attributes)
68
+ super
69
+ end
70
+
71
+
72
+ private
73
+
74
+ def select_changed_attributes_and_collection_keys(json)
75
+ sanitised_json = json.dup
76
+
77
+ sanitised_json.select!{|key, value| changed_attributes.has_key?(key.to_s) || arrayOfPresentHashes?(value) }
78
+
79
+ if sanitised_json.present? # then we'll add an ID so that the resource can be updated
80
+ sanitised_json['id'] = json['id'] if json['id'] && client.can_update_nested_resources?
81
+ end
82
+
83
+ sanitised_json || {}
84
+ end
85
+
86
+ def arrayOfPresentHashes?(value)
87
+ value.is_a?(Array) && value.first.is_a?(Hash) && value.first.present?
88
+ end
89
+
90
+ def unrepresented_collection_ids(key, arrayOfHashes)
91
+ return [] unless client.can_update_nested_resources?
92
+
93
+ all_instance_ids = try(key)&.map(&:id) || []
94
+ represented_instance_ids = arrayOfHashes.map{|item| item["id"] }
95
+
96
+ all_instance_ids.compact - represented_instance_ids.compact
97
+ end
98
+
99
+ def hash_without_root(json)
100
+ self.include_root_in_json ? json[self.element_name] : json
101
+ end
102
+
103
+ def add_root_if_needed(json, root)
104
+ if root and !json.has_key?(root)
105
+ { root => json }
106
+ else
107
+ json
108
+ end
109
+ end
110
+
9
111
  end
10
112
  end
11
- end
113
+ end
@@ -1,50 +1,86 @@
1
1
  module EPlat
2
2
  module Concerns
3
- module OverwriteRequestMethods
3
+ module OverwriteRequestMethods
4
4
 
5
- def find(*arguments)
6
- initialize_singleton!
7
- arguments.second[:params].merge!(client.send "#{element_name}_default_find_args") if arguments.second
8
- arguments.second[:params].merge!(mapping.to_native_keys arguments.second[:params]) if arguments.second
9
-
10
- super
5
+ def self.included(base)
6
+ base.extend ClassMethods
11
7
  end
12
-
13
- def instantiate_collection(collection, original_params = {}, prefix_options = {})
14
- collection = collection["data"] if collection.is_a?(Hash) && collection["data"].present?
15
-
16
- super
17
- end
18
-
19
- def new(attributes, persisted=false)
20
- initialize_singleton!
21
- self.mapping = EPlat::Mapping.new_instance(specifc_mapping: specifc_mapping_name)
22
-
23
- attributes = attributes.send( *top_key_method(mapping.native_top_key) ) if attributes[mapping.native_top_key]
24
- super
25
- end
26
-
27
- def create(attributes = {})
28
- initialize_singleton!
29
- super
30
- end
31
-
32
- def include_root_in_json
33
- client.shopify? && top_level_resource?
34
- end
35
-
36
-
37
- private
38
-
8
+
9
+ module ClassMethods
10
+ def find(*arguments)
11
+ initialize_singleton!
12
+
13
+ if arguments.second && arguments.second[:params]
14
+ arguments.second[:params].merge!(client.try("#{element_name}_default_request_args") || {})
15
+ arguments.second[:params] = mapping.via_native_attributes_where_possible(arguments.second[:params])
16
+ else
17
+ arguments << {params: (client.try("#{element_name}_default_request_args") || {}) }
18
+ end
19
+
20
+ super
21
+ end
22
+
23
+ def find_by(params)
24
+ find(:all, params: params)&.first
25
+ end
26
+
27
+ def instantiate_collection(collection, original_params = {}, prefix_options = {})
28
+ collection = collection["data"] if collection.is_a?(Hash) && collection["data"]
29
+ super
30
+ end
31
+
32
+ def new(attributes, persisted = false)
33
+ initialize_singleton!
34
+
35
+ self.mapping = EPlat::Mapping.new_instance(specifc_mapping: specifc_mapping_name, resource: nil)
36
+
37
+ if attributes[mapping.native_top_key]
38
+ attributes = attributes.send(*top_key_method(mapping.native_top_key))
39
+ end
40
+ attributes = attributes.with_defaults(mapping.class::DEFAULT_VALUES)
41
+
42
+ super
43
+ end
44
+
45
+ def create(attributes = {})
46
+ initialize_singleton!
47
+ attributes = mapping.via_native_attributes_where_possible(attributes)
48
+
49
+ super
50
+ end
51
+
52
+ def update(attributes = {})
53
+ initialize_singleton!
54
+ attributes = mapping.via_native_attributes_where_possible(attributes)
55
+
56
+ super
57
+ end
58
+
59
+ private
60
+
39
61
  def top_key_method(top_key)
40
62
  (top_key == :itself) ? top_key : [:[], top_key]
41
63
  end
42
-
64
+
43
65
  def specifc_mapping_name
44
66
  class_constant_string = self.name.gsub("EPlat::", "")
45
- "EPlat::Mapping::#{ client.platform.capitalize }::V#{ client.api_version.camelize }::#{ class_constant_string }"
67
+ "EPlat::Mapping::#{client.platform.capitalize}::V#{client.api_version.camelize}::#{class_constant_string}"
46
68
  end
69
+ end
70
+
71
+
72
+ def collection_path(options = {})
73
+ options = options.merge client.try("#{element_name}_default_request_args") || {}
74
+
75
+ super(options)
76
+ end
77
+
78
+ def element_path(options = {})
79
+ options = options.merge client.try("#{element_name}_default_request_args") || {}
80
+
81
+ super(options)
82
+ end
47
83
 
48
84
  end
49
85
  end
50
- end
86
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EPlat
4
+ module Countable
5
+
6
+ def count(options = {})
7
+ initialize_singleton!
8
+
9
+ if counts_via_meta_data?
10
+ raw_data = connection.get(collection_path, headers)
11
+ data_hash = ActiveSupport::JSON.decode(raw_data.body)
12
+ data = data_hash.dig("meta", "pagination", "total")
13
+ elsif count_key_next_to_root?
14
+ raw = get(:count, options)
15
+ data = JSON.parse raw.full_response.body
16
+ else
17
+ data = get(:count, options)
18
+ end
19
+
20
+ count =
21
+ case data
22
+ when Hash then data["count"]
23
+ else data
24
+ end
25
+
26
+ Integer(count)
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def counts_via_meta_data?
33
+ return false unless client.bigcommerce?
34
+
35
+ self == EPlat::Product or self == EPlat::Product::Variant
36
+ end
37
+
38
+ def count_key_next_to_root?
39
+ client.bigcommerce? && self == EPlat::Order
40
+ end
41
+
42
+ end
43
+ end