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.
- checksums.yaml +4 -4
- data/README.md +631 -131
- data/lib/active_resource/connection_error.rb +29 -0
- data/lib/active_resource/formats.rb +27 -0
- data/lib/current.rb +1 -1
- data/lib/e_plat/client/default_request_args.rb +27 -3
- data/lib/e_plat/client.rb +114 -87
- data/lib/e_plat/connection.rb +4 -0
- data/lib/e_plat/mapping/base.rb +119 -12
- data/lib/e_plat/mapping/bigcommerce/v_3/metafield.rb +62 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/order/billing_address.rb +14 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/order/line_item.rb +85 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/order/shipping_address.rb +73 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/order.rb +160 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/product/image.rb +12 -12
- data/lib/e_plat/mapping/bigcommerce/v_3/product/variant.rb +1 -1
- data/lib/e_plat/mapping/bigcommerce/v_3/script_tag.rb +78 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/shop.rb +11 -6
- data/lib/e_plat/mapping/bigcommerce/v_3/webhook.rb +54 -0
- data/lib/e_plat/mapping/request_body_root.rb +38 -0
- data/lib/e_plat/mapping/shopify/v_2024_01/metafield.rb +26 -0
- data/lib/e_plat/mapping/shopify/v_2024_01/order/billing_address.rb +14 -0
- data/lib/e_plat/mapping/shopify/v_2024_01/order/shipping_address.rb +30 -0
- data/lib/e_plat/mapping/shopify/{v_2022_07/product.rb → v_2024_01/order.rb} +3 -3
- data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/product/image.rb +1 -1
- data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/product/variant.rb +2 -1
- data/lib/e_plat/mapping/shopify/v_2024_01/product.rb +26 -0
- data/lib/e_plat/mapping/shopify/v_2024_01/script_tag.rb +26 -0
- data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/shop.rb +1 -1
- data/lib/e_plat/mapping/shopify/v_2024_01/webhook.rb +29 -0
- data/lib/e_plat/mapping/virtual_collection/base.rb +14 -0
- data/lib/e_plat/mapping/virtual_collection/bigcommerce/order_line_items.rb +297 -0
- data/lib/e_plat/mapping.rb +3 -3
- data/lib/e_plat/resource/attribute_interface.rb +28 -28
- data/lib/e_plat/resource/base.rb +107 -66
- data/lib/e_plat/resource/collection.rb +92 -0
- data/lib/e_plat/resource/concerns/aliases.rb +102 -18
- data/lib/e_plat/resource/concerns/dirty.rb +54 -0
- data/lib/e_plat/resource/concerns/metafieldable.rb +43 -0
- data/lib/e_plat/resource/concerns/overwrite_instance_methods.rb +108 -6
- data/lib/e_plat/resource/concerns/overwrite_request_methods.rb +73 -37
- data/lib/e_plat/resource/countable.rb +43 -0
- data/lib/e_plat/resource/metafield.rb +70 -0
- data/lib/e_plat/resource/order/Consignment.rb +8 -0
- data/lib/e_plat/resource/order/billing_address.rb +6 -0
- data/lib/e_plat/resource/order/fulfillment.rb +1 -0
- data/lib/e_plat/resource/order/line_item.rb +1 -0
- data/lib/e_plat/resource/order/shipping_address.rb +44 -0
- data/lib/e_plat/resource/order/shipping_line.rb +15 -14
- data/lib/e_plat/resource/order.rb +32 -0
- data/lib/e_plat/resource/paginated/link_headers.rb +42 -0
- data/lib/e_plat/resource/paginated/link_params.rb +26 -0
- data/lib/e_plat/resource/product/image.rb +6 -0
- data/lib/e_plat/resource/product/option.rb +1 -0
- data/lib/e_plat/resource/product/variant.rb +8 -12
- data/lib/e_plat/resource/product.rb +8 -2
- data/lib/e_plat/resource/script_tag.rb +56 -0
- data/lib/e_plat/resource/shop.rb +17 -13
- data/lib/e_plat/resource/shopify_only/recurring_application_charge/usage_charge.rb +32 -0
- data/lib/e_plat/resource/shopify_only/recurring_application_charge.rb +47 -0
- data/lib/e_plat/resource/webhook.rb +50 -0
- data/lib/e_plat/session.rb +10 -7
- data/lib/e_plat/type_coercer.rb +15 -17
- data/lib/e_plat/version.rb +1 -1
- data/lib/e_plat.rb +14 -4
- metadata +67 -7
- 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 =
|
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 #{
|
61
|
+
def #{e_plat_key}=(value)
|
54
62
|
new_value = value
|
55
63
|
#{proc_line}
|
56
|
-
|
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
|
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: "
|
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
|
-
#
|
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
|
-
|
6
|
-
|
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
|
6
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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::#{
|
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
|