e_plat 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/README.md +84 -54
- data/lib/e_plat/client/default_request_args.rb +1 -1
- data/lib/e_plat/client.rb +34 -8
- data/lib/e_plat/mapping/bigcommerce/v_3/product/option.rb +58 -0
- data/lib/e_plat/mapping/bigcommerce/v_3/product/variant/option_value.rb +42 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/metafield.rb +26 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/order/billing_address.rb +14 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/order/shipping_address.rb +30 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/order.rb +26 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/product/image.rb +52 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/product/option.rb +45 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/product/variant/option_value.rb +58 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/product/variant.rb +180 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/product.rb +100 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/script_tag.rb +26 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/shop.rb +26 -0
- data/lib/e_plat/mapping/shopify/v_2024_07/webhook.rb +29 -0
- data/lib/e_plat/mapping/virtual_collection/shopify/product/variant/option_value.rb +272 -0
- data/lib/e_plat/resource/base.rb +24 -2
- data/lib/e_plat/resource/collection.rb +45 -16
- data/lib/e_plat/resource/concerns/aliases.rb +39 -19
- data/lib/e_plat/resource/concerns/dirty.rb +12 -8
- data/lib/e_plat/resource/concerns/graph_q_lable.rb +253 -0
- data/lib/e_plat/resource/concerns/overwrite_instance_methods.rb +4 -4
- data/lib/e_plat/resource/concerns/overwrite_request_methods.rb +1 -1
- data/lib/e_plat/resource/countable.rb +37 -10
- data/lib/e_plat/resource/platform_specific/bigcommerce/product/variant/option_value.rb +2 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product/variant.rb +47 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product/variants_bulk.rb +47 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product.rb +23 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input.rb +98 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/mutation/product/variant.rb +78 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/mutation/product.rb +55 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/mutation.rb +5 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/query/product/variant.rb +44 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/query/product.rb +58 -0
- data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/query.rb +5 -0
- data/lib/e_plat/resource/platform_specific/shopify/product/image.rb +13 -1
- data/lib/e_plat/resource/platform_specific/shopify/product/option.rb +13 -1
- data/lib/e_plat/resource/platform_specific/shopify/product/variant/option_value.rb +14 -0
- data/lib/e_plat/resource/platform_specific/shopify/product/variant.rb +65 -1
- data/lib/e_plat/resource/platform_specific/shopify/product.rb +77 -2
- data/lib/e_plat/resource/product/image.rb +8 -5
- data/lib/e_plat/resource/product/option.rb +2 -3
- data/lib/e_plat/resource/product/variant/option_value.rb +17 -0
- data/lib/e_plat/resource/product/variant.rb +4 -10
- data/lib/e_plat/resource/product.rb +23 -11
- data/lib/e_plat/session.rb +3 -2
- data/lib/e_plat/type_coercer.rb +3 -1
- data/lib/e_plat/version.rb +1 -1
- data/lib/e_plat.rb +12 -3
- data/lib/hash.rb +39 -0
- metadata +130 -27
@@ -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
|
-
@
|
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
|
-
@
|
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(@
|
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(@
|
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(@
|
38
|
+
fetch_page(@next)
|
33
39
|
end
|
34
40
|
|
35
41
|
def fetch_previous_page
|
36
42
|
return unless pagination_available?
|
37
|
-
fetch_page(@
|
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
|
-
|
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
|
71
|
-
|
72
|
-
|
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
|
-
|
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(
|
78
|
-
return unless
|
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(
|
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
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
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:
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
160
|
+
#{ will_change_statement }
|
141
161
|
#{change_statements.join("\n")}
|
142
162
|
STRING
|
143
163
|
end
|
@@ -11,16 +11,20 @@ module EPlat
|
|
11
11
|
@changed_attributes ||= {}
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
# attribute will change adds to changed_attributes.
|
15
|
+
# We support a force_value option, so we can bipass the getter if the e_plat key is the same as the native key
|
16
|
+
# this lets us pass in the native_setter result directly from native_attribute_will_change!
|
17
|
+
def attribute_will_change!(attr, force_value: nil)
|
15
18
|
changed_attributes[attr] = read_attribute(attr)
|
16
|
-
|
17
|
-
native_keys.include?(attr) ? e_plat_attribute_will_change!(attr) : native_attribute_will_change!(attr)
|
19
|
+
|
20
|
+
native_keys.include?(attr) ? e_plat_attribute_will_change!(attr, force_value:) : native_attribute_will_change!(attr, force_value:)
|
18
21
|
end
|
19
22
|
|
20
23
|
def attribute_changed?(attr)
|
21
24
|
changed_attributes.include?(attr)
|
22
25
|
end
|
23
26
|
|
27
|
+
|
24
28
|
def read_attribute(attr)
|
25
29
|
self.send(attr)
|
26
30
|
end
|
@@ -33,18 +37,18 @@ module EPlat
|
|
33
37
|
|
34
38
|
private
|
35
39
|
|
36
|
-
def native_attribute_will_change!(eplat_attr)
|
40
|
+
def native_attribute_will_change!(eplat_attr, force_value: nil)
|
37
41
|
key = self.mapping.native_key_to(eplat_attr)
|
38
42
|
return unless key && self.respond_to?(key)
|
39
43
|
|
40
|
-
changed_attributes[key] = send(key)
|
44
|
+
changed_attributes[key] = force_value || send(key)
|
41
45
|
end
|
42
46
|
|
43
|
-
def e_plat_attribute_will_change!(native_attr)
|
47
|
+
def e_plat_attribute_will_change!(native_attr, force_value: nil)
|
44
48
|
key = self.mapping.e_plat_key_to(native_attr)
|
45
49
|
return unless key && self.respond_to?(key)
|
46
|
-
|
47
|
-
changed_attributes[key] = send(key)
|
50
|
+
|
51
|
+
changed_attributes[key] = force_value || send(key)
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EPlat
|
4
|
+
module Concerns::GraphQLable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :before_graphql_callbacks, default: {}
|
9
|
+
end
|
10
|
+
|
11
|
+
FILTER_ARGS = [:id, :after, :before, :last, :first].freeze
|
12
|
+
QUERY_ARG_ARGS = [:available_for_sale, :created_at, :product_type, :product_id, :tag, :tag_not, :title, :updated_at, :vendor].freeze
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
|
16
|
+
def requests_via_graphql(with: {}, **options)
|
17
|
+
return if (options[:unless] && options[:unless].call)
|
18
|
+
|
19
|
+
with.each do |action, graphql_proc|
|
20
|
+
graphql_proc = graphql_proc.gsub("{API_VERSION}", EPlat.shopify_graphql_version)
|
21
|
+
|
22
|
+
if graphql_proc.to_s.include?("::Query")
|
23
|
+
define_singleton_graphql_method(action, graphql_proc)
|
24
|
+
elsif graphql_proc.to_s.include?("::Mutation")
|
25
|
+
define_instance_graphql_method(action, graphql_proc)
|
26
|
+
else
|
27
|
+
raise EPlat::Error.new("Invalid graphql request type")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_to_graphql_requests(action, graphql_proc)
|
33
|
+
before_graphql_callbacks[action] = graphql_proc
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_singleton_graphql_method(action, graphql_proc)
|
37
|
+
define_singleton_method(action) do |*args|
|
38
|
+
initialize_singleton!
|
39
|
+
query_graphql(action, graphql_proc, *args)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_instance_graphql_method(action, graphql_proc)
|
44
|
+
define_method(action) do |*args|
|
45
|
+
self.class.initialize_singleton!
|
46
|
+
mutate_graphql(action, graphql_proc, *args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def query_graphql(action, graphql_proc, *args)
|
51
|
+
graphql = eval graphql_proc
|
52
|
+
args = scope_with_options_hash(args, action)
|
53
|
+
|
54
|
+
response = execute_graphql_request graphql_query_with_args(graphql, args)
|
55
|
+
|
56
|
+
if !response.is_a?(Hash)
|
57
|
+
raise EPlat::GraphqlError.new(response.first["message"])
|
58
|
+
elsif response["errors"]
|
59
|
+
raise EPlat::GraphqlError.new(response["errors"].first["message"])
|
60
|
+
else
|
61
|
+
case action
|
62
|
+
when :find_one, :find_single
|
63
|
+
instantiate_record resources_parsed_from(response)
|
64
|
+
else
|
65
|
+
instantiate_collection resources_parsed_from(response)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
rescue StandardError => e
|
69
|
+
raise EPlat::GraphqlError.new(e.message)
|
70
|
+
end
|
71
|
+
|
72
|
+
def execute_graphql_request(graphql_string)
|
73
|
+
puts graphql_string if EPlat.config.print_graphql_requests
|
74
|
+
|
75
|
+
format.decode connection.post(
|
76
|
+
client.graphql_request_url,
|
77
|
+
{ query: graphql_string }.to_json,
|
78
|
+
headers
|
79
|
+
).body
|
80
|
+
end
|
81
|
+
|
82
|
+
#utils
|
83
|
+
|
84
|
+
def scope_with_options_hash(args, action)
|
85
|
+
if args.length > 1
|
86
|
+
scope, options = args.slice!(0), args.slice!(0)
|
87
|
+
elsif args.first.is_a?(Hash)
|
88
|
+
scope, options = nil, args.slice!(0) || {}
|
89
|
+
else
|
90
|
+
scope, options = args.slice!(0), {}
|
91
|
+
end
|
92
|
+
|
93
|
+
scope_arg =
|
94
|
+
case action
|
95
|
+
when :find_single
|
96
|
+
{id: formatted_id(scope)}
|
97
|
+
when :find_one
|
98
|
+
{first: 1}
|
99
|
+
when :find_every
|
100
|
+
{first: scope || 200}
|
101
|
+
end
|
102
|
+
|
103
|
+
(options[:params] || {}).with_defaults(scope_arg)
|
104
|
+
end
|
105
|
+
|
106
|
+
def graphql_query_with_args(graphql, args)
|
107
|
+
params = filter_graphql_args(**args).merge(
|
108
|
+
query: graphql_query_arg(**args)
|
109
|
+
).compact_blank
|
110
|
+
|
111
|
+
graphql.call(params.to_graphql_args)
|
112
|
+
end
|
113
|
+
|
114
|
+
def filter_graphql_args(**args)
|
115
|
+
arguments = sanitize_filter_args args.with_indifferent_access
|
116
|
+
|
117
|
+
arguments.slice(*FILTER_ARGS).deep_stringify_keys
|
118
|
+
end
|
119
|
+
|
120
|
+
def sanitize_filter_args(args)
|
121
|
+
args.transform_keys!{|key| (key == "limit") ? "first" : key}
|
122
|
+
args.transform_keys!{|key| (key == "first") ? "last" : key} if args.include?("before")
|
123
|
+
args.delete("last") if args.include?("first") && args.include?("last")
|
124
|
+
|
125
|
+
args
|
126
|
+
end
|
127
|
+
|
128
|
+
def graphql_query_arg(**args)
|
129
|
+
args.with_indifferent_access.slice(*QUERY_ARG_ARGS).to_shopify_graphql_query_string
|
130
|
+
end
|
131
|
+
|
132
|
+
def resources_parsed_from(response)
|
133
|
+
remove_nodes_from(response).then{remove_root_from _1}.then{ snake_case_keys _1}
|
134
|
+
end
|
135
|
+
|
136
|
+
def remove_nodes_from(data)
|
137
|
+
return data unless data.is_a?(Hash) or data.is_a?(Array)
|
138
|
+
|
139
|
+
if data.is_a?(Array)
|
140
|
+
return data.map { |item| remove_nodes_from(item) }
|
141
|
+
end
|
142
|
+
|
143
|
+
data.transform_values do |value|
|
144
|
+
if value.is_a?(Hash) && value.key?('nodes')
|
145
|
+
remove_nodes_from(value['nodes'])
|
146
|
+
elsif value.is_a?(Array)
|
147
|
+
value.map { |item| remove_nodes_from(item) }
|
148
|
+
else
|
149
|
+
remove_nodes_from(value)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def remove_root_from(data)
|
155
|
+
return data unless data.is_a?(Hash)
|
156
|
+
|
157
|
+
if data.has_key?(graphql_element_name)
|
158
|
+
data[graphql_element_name]
|
159
|
+
elsif data.has_key?(graphql_collection_name)
|
160
|
+
data[graphql_collection_name]
|
161
|
+
else
|
162
|
+
data
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def snake_case_keys(data)
|
167
|
+
return data unless data.is_a?(Hash) or data.is_a?(Array)
|
168
|
+
|
169
|
+
if data.is_a?(Array)
|
170
|
+
return data.map { |item| snake_case_keys(item) }
|
171
|
+
end
|
172
|
+
|
173
|
+
data.deep_transform_keys(&:underscore)
|
174
|
+
end
|
175
|
+
|
176
|
+
def graphql_element_name
|
177
|
+
case element_name
|
178
|
+
when "variant"
|
179
|
+
"productVariant"
|
180
|
+
else
|
181
|
+
element_name
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def graphql_collection_name
|
186
|
+
case collection_name
|
187
|
+
when "variants"
|
188
|
+
"productVariants"
|
189
|
+
else
|
190
|
+
collection_name
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def formatted_id(id)
|
195
|
+
return id unless client.shopify? && client.api_version != "2024_01"
|
196
|
+
return if id.to_s.include?("gid://")
|
197
|
+
|
198
|
+
case element_name
|
199
|
+
when "product"
|
200
|
+
"gid://shopify/Product/#{id}"
|
201
|
+
when "variant"
|
202
|
+
"gid://shopify/ProductVariant/#{id}"
|
203
|
+
when "image"
|
204
|
+
"gid://shopify/ProductImage/#{id}"
|
205
|
+
else
|
206
|
+
raise EPlat::Errors.new("Unsupported element name")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
def mutate_graphql(action, graphql_proc_constant, *args)
|
213
|
+
callback_name = (action == :delete) ? :destroy : action
|
214
|
+
|
215
|
+
run_callbacks callback_name.to_sym do
|
216
|
+
response = self.class.execute_graphql_request graphql_mutation_string(graphql_proc_constant, action)
|
217
|
+
|
218
|
+
if !response.is_a?(Hash)
|
219
|
+
raise EPlat::GraphqlError.new(response.first["message"])
|
220
|
+
elsif response["errors"]
|
221
|
+
raise EPlat::GraphqlError.new(response["errors"].first["message"])
|
222
|
+
else
|
223
|
+
response = remove_mutation_root_from(response, graphql_proc_constant)
|
224
|
+
load self.class.resources_parsed_from(response), true, true
|
225
|
+
|
226
|
+
@persisted = true
|
227
|
+
changed_attributes.clear
|
228
|
+
end
|
229
|
+
end
|
230
|
+
rescue StandardError => e
|
231
|
+
raise EPlat::GraphqlError.new(e.message)
|
232
|
+
end
|
233
|
+
|
234
|
+
def graphql_mutation_string(graphql_proc_constant, action)
|
235
|
+
mutation_proc = eval graphql_proc_constant
|
236
|
+
additional_graphql = self.class.before_graphql_callbacks[action.to_sym]&.call(self)
|
237
|
+
input =
|
238
|
+
if action == :delete
|
239
|
+
self.is_a?(EPlat::Product::Variant) ? graphql_input.new(id: formatted_id(id)) : graphql_input.new({id: formatted_id(id)})
|
240
|
+
else
|
241
|
+
graphql_input.new mapping.via_native_attributes_where_possible(self.class.remove_root_from as_json)
|
242
|
+
end
|
243
|
+
|
244
|
+
mutation_proc.call(input.to_graphql_args, additional_graphql:)
|
245
|
+
end
|
246
|
+
|
247
|
+
def remove_mutation_root_from(data, graphql_proc_constant)
|
248
|
+
mutation_root = graphql_proc_constant.split(".").last.camelcase(:lower)
|
249
|
+
(mutation_root && data[mutation_root]) ? data[mutation_root] : data
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
@@ -84,14 +84,14 @@ module EPlat
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def arrayOfPresentHashes?(value)
|
87
|
-
value.is_a?(Array) && value.first.is_a?(Hash) && value.
|
87
|
+
value.is_a?(Array) && value.first.is_a?(Hash) && value.any?(&:present?)
|
88
88
|
end
|
89
89
|
|
90
|
-
def unrepresented_collection_ids(key,
|
90
|
+
def unrepresented_collection_ids(key, array_of_hashes)
|
91
91
|
return [] unless client.can_update_nested_resources?
|
92
92
|
|
93
|
-
all_instance_ids =
|
94
|
-
represented_instance_ids =
|
93
|
+
all_instance_ids = send(key).map{|resource| resource.formatted_id resource.id} || []
|
94
|
+
represented_instance_ids = array_of_hashes.map{|item| item["id"] }
|
95
95
|
|
96
96
|
all_instance_ids.compact - represented_instance_ids.compact
|
97
97
|
end
|
@@ -15,7 +15,7 @@ module EPlat
|
|
15
15
|
else
|
16
16
|
initialize_singleton!
|
17
17
|
|
18
|
-
if arguments.second && arguments.second[:params]
|
18
|
+
if arguments.second && arguments.second.is_a?(Hash) && arguments.second[:params]
|
19
19
|
arguments.second[:params].merge!(client.try("#{element_name}_default_request_args") || {})
|
20
20
|
arguments.second[:params] = mapping.via_native_attributes_where_possible(arguments.second[:params])
|
21
21
|
else
|
@@ -6,16 +6,19 @@ module EPlat
|
|
6
6
|
def count(options = {})
|
7
7
|
initialize_singleton!
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
data =
|
10
|
+
if counts_via_graphql?
|
11
|
+
graphql_count
|
12
|
+
elsif counts_via_meta_data?
|
13
|
+
raw_data = connection.get(collection_path, headers)
|
14
|
+
data_hash = ActiveSupport::JSON.decode(raw_data.body)
|
15
|
+
data_hash.dig("meta", "pagination", "total")
|
16
|
+
elsif count_key_next_to_root?
|
17
|
+
raw = get(:count, options)
|
18
|
+
JSON.parse(raw.full_response.body)
|
19
|
+
else
|
20
|
+
get(:count, options)
|
21
|
+
end
|
19
22
|
|
20
23
|
count =
|
21
24
|
case data
|
@@ -29,6 +32,13 @@ module EPlat
|
|
29
32
|
|
30
33
|
private
|
31
34
|
|
35
|
+
def counts_via_graphql?
|
36
|
+
return unless client.shopify?
|
37
|
+
return if client.api_version == "2024_01"
|
38
|
+
|
39
|
+
self == EPlat::Product or self == EPlat::Shopify::Product
|
40
|
+
end
|
41
|
+
|
32
42
|
def counts_via_meta_data?
|
33
43
|
return false unless client.bigcommerce?
|
34
44
|
|
@@ -39,5 +49,22 @@ module EPlat
|
|
39
49
|
client.bigcommerce? && self == EPlat::Order
|
40
50
|
end
|
41
51
|
|
52
|
+
def graphql_count_query
|
53
|
+
"EPlat::Shopify::GraphQL::#{EPlat.shopify_graphql_version}::Query.products_count"
|
54
|
+
end
|
55
|
+
|
56
|
+
def graphql_count
|
57
|
+
graphql_query = eval graphql_count_query
|
58
|
+
response = execute_graphql_request(graphql_query.call(""))
|
59
|
+
|
60
|
+
if !response.is_a?(Hash)
|
61
|
+
raise EPlat::GraphqlError.new(response.first["message"])
|
62
|
+
elsif response["errors"]
|
63
|
+
raise EPlat::GraphqlError.new(response["errors"].first["message"])
|
64
|
+
else
|
65
|
+
response["productsCount"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
42
69
|
end
|
43
70
|
end
|
data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2024_07/input/product/variant.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class EPlat::Shopify::GraphQL::V202407::Input::Product::Variant < EPlat::Shopify::GraphQL::V202407::Input
|
2
|
+
SUPPORTED_FIELDS = %w[
|
3
|
+
barcode
|
4
|
+
compareAtPrice
|
5
|
+
id
|
6
|
+
inventoryItem
|
7
|
+
inventoryPolicy
|
8
|
+
inventoryQuantities
|
9
|
+
mediaId
|
10
|
+
mediaSrc
|
11
|
+
metafields
|
12
|
+
optionValues
|
13
|
+
price
|
14
|
+
taxCode
|
15
|
+
taxable
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
SUPPORTED_NAMED_ARGUMENT_FIELDS = {
|
19
|
+
variant: %w[
|
20
|
+
barcode
|
21
|
+
compareAtPrice
|
22
|
+
id
|
23
|
+
inventoryItem
|
24
|
+
inventoryPolicy
|
25
|
+
inventoryQuantities
|
26
|
+
mediaId
|
27
|
+
mediaSrc
|
28
|
+
metafields
|
29
|
+
optionValues
|
30
|
+
price
|
31
|
+
taxCode
|
32
|
+
taxable
|
33
|
+
],
|
34
|
+
inventory_item: %w[
|
35
|
+
cost
|
36
|
+
countryCodeOfOrigin
|
37
|
+
countryHarmonizedSystemCodes
|
38
|
+
harmonizedSystemCode
|
39
|
+
measurement
|
40
|
+
provinceCodeOfOrigin
|
41
|
+
requiresShipping
|
42
|
+
sku
|
43
|
+
tracked
|
44
|
+
]
|
45
|
+
}.with_indifferent_access.freeze
|
46
|
+
|
47
|
+
end
|