e_plat 0.3.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +631 -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 +14 -4
  66. metadata +67 -7
  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