e_plat 1.2.0 → 1.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -8
  3. data/lib/e_plat/mapping/shopify/v_2026_04/metafield.rb +26 -0
  4. data/lib/e_plat/mapping/shopify/v_2026_04/order/billing_address.rb +14 -0
  5. data/lib/e_plat/mapping/shopify/v_2026_04/order/shipping_address.rb +30 -0
  6. data/lib/e_plat/mapping/shopify/v_2026_04/order.rb +26 -0
  7. data/lib/e_plat/mapping/shopify/v_2026_04/product/image.rb +52 -0
  8. data/lib/e_plat/mapping/shopify/v_2026_04/product/option.rb +45 -0
  9. data/lib/e_plat/mapping/shopify/v_2026_04/product/variant/option_value.rb +58 -0
  10. data/lib/e_plat/mapping/shopify/v_2026_04/product/variant.rb +182 -0
  11. data/lib/e_plat/mapping/shopify/v_2026_04/product.rb +100 -0
  12. data/lib/e_plat/mapping/shopify/v_2026_04/script_tag.rb +26 -0
  13. data/lib/e_plat/mapping/shopify/v_2026_04/shop.rb +26 -0
  14. data/lib/e_plat/mapping/shopify/v_2026_04/webhook.rb +29 -0
  15. data/lib/e_plat/resource/concerns/graph_q_lable.rb +15 -0
  16. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/input/product/variant.rb +87 -0
  17. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/input/product/variants_bulk.rb +58 -0
  18. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/input/product.rb +36 -0
  19. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/input.rb +133 -0
  20. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/mutation/product/variant.rb +86 -0
  21. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/mutation/product.rb +55 -0
  22. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/mutation.rb +5 -0
  23. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/query/product/variant.rb +44 -0
  24. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/query/product.rb +58 -0
  25. data/lib/e_plat/resource/platform_specific/shopify/graph_q_l/v_2026_04/query.rb +5 -0
  26. data/lib/e_plat/version.rb +1 -1
  27. data/lib/e_plat.rb +58 -58
  28. metadata +25 -7
@@ -60,11 +60,15 @@ module EPlat
60
60
  else
61
61
  case action
62
62
  when :find_one, :find_single
63
+ raise_resource_not_found! if resource_missing_from?(response)
64
+
63
65
  instantiate_record resources_parsed_from(response)
64
66
  else
65
67
  instantiate_collection resources_parsed_from(response)
66
68
  end
67
69
  end
70
+ rescue ActiveResource::ResourceNotFound
71
+ raise
68
72
  rescue StandardError => e
69
73
  raise EPlat::GraphqlError.new(e.message)
70
74
  end
@@ -79,6 +83,17 @@ module EPlat
79
83
  ).body
80
84
  end
81
85
 
86
+ # a 200 with a null resource is Shopify graphql's version of a 404
87
+ def resource_missing_from?(response)
88
+ data = response["data"].is_a?(Hash) ? response["data"] : response
89
+
90
+ data.key?(graphql_element_name) && data[graphql_element_name].nil?
91
+ end
92
+
93
+ def raise_resource_not_found!
94
+ raise ActiveResource::ResourceNotFound.new(nil, "#{name} not found")
95
+ end
96
+
82
97
  #utils
83
98
 
84
99
  def scope_with_options_hash(args, action)
@@ -0,0 +1,87 @@
1
+ class EPlat::Shopify::GraphQL::V202604::Input::Product::Variant < EPlat::Shopify::GraphQL::V202604::Input
2
+ USES_ELEMENT_NAME_AS_INPUT_ROOT_KEY = false
3
+
4
+ SUPPORTED_FIELDS = %w[
5
+ barcode
6
+ compareAtPrice
7
+ id
8
+ inventoryItem
9
+ inventoryPolicy
10
+ inventoryQuantities
11
+ mediaSrc
12
+ metafields
13
+ optionValues
14
+ price
15
+ taxable
16
+ ].freeze
17
+
18
+ SUPPORTED_NAMED_ARGUMENT_FIELDS = {
19
+ product_id: %w[
20
+ productId
21
+ ],
22
+ variant: %w[
23
+ barcode
24
+ compareAtPrice
25
+ id
26
+ inventoryItem
27
+ inventoryPolicy
28
+ inventoryQuantities
29
+ mediaSrc
30
+ metafields
31
+ optionValues
32
+ price
33
+ taxable
34
+ ],
35
+ variants: %w[
36
+ barcode
37
+ compareAtPrice
38
+ id
39
+ inventoryItem
40
+ inventoryPolicy
41
+ inventoryQuantities
42
+ mediaSrc
43
+ metafields
44
+ optionValues
45
+ price
46
+ taxable
47
+ ],
48
+ inventory_item: %w[
49
+ cost
50
+ countryCodeOfOrigin
51
+ countryHarmonizedSystemCodes
52
+ harmonizedSystemCode
53
+ measurement
54
+ provinceCodeOfOrigin
55
+ requiresShipping
56
+ sku
57
+ tracked
58
+ ],
59
+ option_values: %w[
60
+ optionId
61
+ name
62
+ linkedMetafieldValue
63
+ optionId
64
+ optionName
65
+ ]
66
+ }.with_indifferent_access.freeze
67
+
68
+
69
+ def self.mutation_input(resource, action)
70
+ case action.to_sym
71
+ when :delete
72
+ new(
73
+ variantsIds: [resource.formatted_id],
74
+ product_id: EPlat::Product.formatted_id(resource.product_id)
75
+ )
76
+ else
77
+ new(
78
+ variants: [
79
+ resource.class.mapping.via_native_attributes_where_possible(
80
+ resource.class.remove_root_from(resource.as_json)
81
+ )
82
+ ],
83
+ product_id: EPlat::Product.formatted_id(resource.product_id)
84
+ )
85
+ end.query_input
86
+ end
87
+ end
@@ -0,0 +1,58 @@
1
+ class EPlat::Shopify::GraphQL::V202604::Input::Product::VariantsBulk < EPlat::Shopify::GraphQL::V202604::Input
2
+ USES_ELEMENT_NAME_AS_INPUT_ROOT_KEY = false
3
+
4
+ SUPPORTED_FIELDS = %w[
5
+ barcode
6
+ compareAtPrice
7
+ id
8
+ inventoryItem
9
+ inventoryPolicy
10
+ inventoryQuantities
11
+ mediaSrc
12
+ metafields
13
+ optionValues
14
+ price
15
+ taxable
16
+ ].freeze
17
+
18
+ SUPPORTED_NAMED_ARGUMENT_FIELDS = {
19
+ variants: %w[
20
+ barcode
21
+ compareAtPrice
22
+ id
23
+ inventoryItem
24
+ inventoryPolicy
25
+ inventoryQuantities
26
+ mediaSrc
27
+ metafields
28
+ optionValues
29
+ price
30
+ taxable
31
+ ],
32
+ inventory_item: %w[
33
+ cost
34
+ countryCodeOfOrigin
35
+ countryHarmonizedSystemCodes
36
+ harmonizedSystemCode
37
+ measurement
38
+ provinceCodeOfOrigin
39
+ requiresShipping
40
+ sku
41
+ tracked
42
+ ],
43
+ option_values: %w[
44
+ name
45
+ optionId
46
+ linkedMetafieldValue
47
+ optionId
48
+ optionName
49
+ ]
50
+ }.with_indifferent_access.freeze
51
+
52
+ def self.mutation_input(product, action)
53
+ new(
54
+ product_id: EPlat::Product.formatted_id(product.id),
55
+ variants: product.as_json["product"]["variants"]
56
+ ).query_input
57
+ end
58
+ end
@@ -0,0 +1,36 @@
1
+ class EPlat::Shopify::GraphQL::V202604::Input::Product < EPlat::Shopify::GraphQL::V202604::Input
2
+
3
+ SUPPORTED_FIELDS = %w[
4
+ category
5
+ claimOwnership
6
+ title
7
+ descriptionHtml
8
+ productType
9
+ vendor
10
+ tags
11
+ collectionsToJoin
12
+ collectionsToLeave
13
+ combinedListingRole
14
+ metafields
15
+ productOptions
16
+ templateSuffix
17
+ requiresSellingPlan
18
+ giftCard
19
+ giftCardTemplateSuffix
20
+ seo
21
+ status
22
+ id
23
+ handle
24
+ redirectNewHandle
25
+ ].freeze
26
+
27
+ def self.mutation_input(resource, action)
28
+ case action.to_sym
29
+ when :delete
30
+ new(input: {id: resource.formatted_id})
31
+ else
32
+ rootless_json = resource.class.remove_root_from(resource.as_json)
33
+ new(resource.class.mapping.via_native_attributes_where_possible rootless_json)
34
+ end.query_input
35
+ end
36
+ end
@@ -0,0 +1,133 @@
1
+ class EPlat::Shopify::GraphQL::V202604::Input
2
+ attr_accessor :input, :named_arguments
3
+
4
+ SUPPORTED_FIELDS = []
5
+ SUPPORTED_NAMED_ARGUMENT_FIELDS = {}
6
+ USES_ELEMENT_NAME_AS_INPUT_ROOT_KEY = true
7
+
8
+ def initialize(input = {}, **named_arguments)
9
+ @input = input.deep_transform_keys { |key| key.to_s.camelize(:lower) }
10
+ @named_arguments = named_arguments.deep_transform_keys { |key| key.to_s.camelize(:lower) }
11
+ end
12
+
13
+ def query_input
14
+ args =
15
+ if named_arguments.present?
16
+ named_arguments_with_allowed_fields(named_arguments)
17
+ else
18
+ self.class::USES_ELEMENT_NAME_AS_INPUT_ROOT_KEY ? { element_name => allowed_fields } : allowed_fields
19
+ end
20
+
21
+ format_graphql_args(args)
22
+ end
23
+
24
+ def self.mutation_input(resource, action)
25
+ raise "mutation_input not implemented for #{self.class.name}"
26
+ # example:
27
+ # case action.to_sym
28
+ # when :delete
29
+ # new(input: { id: resource.formatted_id })
30
+ # else
31
+ # new(resource.class.mapping.via_native_attributes_where_possible(
32
+ # resource.class.remove_root_from(resource.as_json)
33
+ # ))
34
+ # end.query_input
35
+ end
36
+
37
+
38
+ protected
39
+
40
+ def allowed_fields
41
+ fields_hash = input.slice(*self.class::SUPPORTED_FIELDS)
42
+
43
+ fields_hash.map do |key, value|
44
+ if value.is_a?(Hash) && child_allowed_fields = self.class::SUPPORTED_NAMED_ARGUMENT_FIELDS[key] || self.class::SUPPORTED_NAMED_ARGUMENT_FIELDS[key&.underscore]
45
+ [key, value.slice(*child_allowed_fields)]
46
+ else
47
+ [key, value]
48
+ end
49
+ end.to_h
50
+ end
51
+
52
+ def named_arguments_with_allowed_fields(arguments, name=nil)
53
+ arguments.map do |key, value|
54
+ fields_hash = self.class::SUPPORTED_NAMED_ARGUMENT_FIELDS[name] || self.class::SUPPORTED_NAMED_ARGUMENT_FIELDS[key] || self.class::SUPPORTED_NAMED_ARGUMENT_FIELDS[key&.underscore] || []
55
+
56
+ case value
57
+ when Hash
58
+ [key, named_arguments_with_allowed_fields(value, key.underscore)]
59
+ when Array
60
+ [key, value.map {_1.is_a?(Hash) ? named_arguments_with_allowed_fields(_1, key.underscore) : _1}]
61
+ else
62
+ (fields_hash.empty? || fields_hash.include?(key)) ? [key, value] : [nil, nil]
63
+ end
64
+ end.to_h.compact_blank
65
+ end
66
+
67
+ def format_graphql_item(item, key: nil)
68
+ case item
69
+ when Hash
70
+ "{ #{item.map { |k, v| "#{k}: #{v.is_a?(String) ? enum_friendly(v, key: k) : format_graphql_item(v, key: k)}" }.join(', ')} }"
71
+ when Array
72
+ "[ #{item.map { |i| format_graphql_item(i, key: key) }.join(', ')} ]"
73
+ else
74
+ enum_friendly(item, key: key)
75
+ end
76
+ end
77
+
78
+ def enum_friendly(value, key: nil)
79
+ case
80
+ when key == "tags"
81
+ "\"#{single_quotes_only(value)}\""
82
+ when value.is_a?(String)
83
+ if value.match?(/\A[A-Z_]+\z/)
84
+ value
85
+ else
86
+ "\"#{single_quotes_only(value)}\""
87
+ end
88
+ else
89
+ value
90
+ end
91
+ end
92
+
93
+ def single_quotes_only(value)
94
+ value&.gsub("\"", "'")
95
+ end
96
+
97
+ def format_graphql_args(args, key: nil)
98
+ args.map do |key, value|
99
+ case value
100
+ when Hash
101
+ "#{key}: { #{format_graphql_args(value, key:)} }"
102
+ when Array
103
+ if (value.first.is_a? Hash)
104
+ "#{key}: [ #{value.map { |v| "{ #{format_graphql_args(v, key:)} }" }.join(', ')} ]"
105
+ else
106
+ "#{key}: [ #{value.map { |v| enum_friendly(v, key: key) }.join(', ')} ]"
107
+ end
108
+ when String
109
+ "#{key}: #{enum_friendly(value, key: key)}"
110
+ else
111
+ "#{key}: #{value}"
112
+ end
113
+ end.join(', ')
114
+ end
115
+
116
+ def element_name
117
+ self.class.name.split("::").last.downcase
118
+ end
119
+
120
+ end
121
+ #
122
+ # creates a graphql mutation input.
123
+ # if just input as arg, use:
124
+ # input.new(
125
+ # [{...}, {...}]
126
+ # ).to_graphql_args
127
+ #
128
+ # Otherwise, you can pass named arguments in for each graphql argument:
129
+ # input.new(
130
+ # product_id: "gid/123",
131
+ # variants: [{...}, {...}]
132
+ # ).to_graphql_args
133
+ #
@@ -0,0 +1,86 @@
1
+
2
+ module EPlat::Shopify::GraphQL::V202604::Mutation::Product::Variant
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ # def self.product_variant_delete
8
+ # ->(args, additional_graphql: nil) do
9
+ # <<~GRAPHQL
10
+ # mutation {
11
+ # #{additional_graphql}
12
+ # productVariantDelete(#{args}) {
13
+ # deletedProductVariantId
14
+ # userErrors {
15
+ # field
16
+ # message
17
+ # }
18
+ # }
19
+ # }
20
+ # GRAPHQL
21
+ # end
22
+ # end
23
+
24
+ def self.product_variants_bulk_update
25
+ ->(args, additional_graphql: nil) do
26
+ <<~GRAPHQL
27
+ mutation {
28
+ #{product_variants_bulk_update_fragment.call(args, additional_graphql: additional_graphql)}
29
+ }
30
+ GRAPHQL
31
+ end
32
+ end
33
+
34
+ def self.product_variants_bulk_create
35
+ ->(args, additional_graphql: nil) do
36
+ <<~GRAPHQL
37
+ mutation {
38
+ #{product_variants_bulk_create_fragment.call(args, additional_graphql: additional_graphql)}
39
+ }
40
+ GRAPHQL
41
+ end
42
+ end
43
+
44
+ def self.product_variants_bulk_delete
45
+ ->(args, additional_graphql: nil) do
46
+ <<~GRAPHQL
47
+ mutation {
48
+ #{additional_graphql}
49
+ productVariantsBulkDelete(#{args}) {
50
+ product {
51
+ id
52
+ }
53
+ }
54
+ }
55
+ GRAPHQL
56
+ end
57
+ end
58
+
59
+ def self.product_variants_bulk_create_fragment
60
+ ->(args, additional_graphql: nil) do
61
+ <<~GRAPHQL
62
+ #{additional_graphql}
63
+ productVariantsBulkCreate(#{args}) {
64
+ productVariants {
65
+ #{EPlat::Shopify::Product::Variant.graphql_fields}
66
+ }
67
+ }
68
+ GRAPHQL
69
+ end
70
+ end
71
+
72
+ def self.product_variants_bulk_update_fragment
73
+ ->(args, additional_graphql: nil) do
74
+ <<~GRAPHQL
75
+ #{additional_graphql}
76
+ productVariantsBulkUpdate(#{args}) {
77
+ productVariants {
78
+ #{EPlat::Shopify::Product::Variant.graphql_fields}
79
+ }
80
+ }
81
+ GRAPHQL
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,55 @@
1
+
2
+ module EPlat::Shopify::GraphQL::V202604::Mutation::Product
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ def self.product_create
8
+ ->(args, additional_graphql: nil) do
9
+ <<~GRAPHQL
10
+ mutation {
11
+ #{additional_graphql}
12
+ productCreate(#{args}) {
13
+ product {
14
+ #{EPlat::Shopify::Product.graphql_fields}
15
+ }
16
+ }
17
+ }
18
+ GRAPHQL
19
+ end
20
+ end
21
+
22
+ def self.product_update
23
+ ->(args, additional_graphql: nil) do
24
+ <<~GRAPHQL
25
+ mutation {
26
+ #{additional_graphql}
27
+ productUpdate(#{args}) {
28
+ product {
29
+ #{EPlat::Shopify::Product.graphql_fields}
30
+ }
31
+ }
32
+ }
33
+ GRAPHQL
34
+ end
35
+ end
36
+
37
+ def self.product_delete
38
+ ->(args, additional_graphql: nil) do
39
+ <<~GRAPHQL
40
+ mutation {
41
+ #{additional_graphql}
42
+ productDelete(#{args}) {
43
+ deletedProductId
44
+ userErrors {
45
+ field
46
+ message
47
+ }
48
+ }
49
+ }
50
+ GRAPHQL
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ class EPlat::Shopify::GraphQL::V202604::Mutation
2
+ include Product, Product::Variant
3
+ end
4
+
5
+
@@ -0,0 +1,44 @@
1
+
2
+ module EPlat::Shopify::GraphQL::V202604::Query::Product::Variant
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ def self.variants
8
+ ->(args) do
9
+ formatted_args = args.present? ? "(#{args})" : ""
10
+
11
+ <<~GRAPHQL
12
+ query getProductVariants {
13
+ productVariants#{formatted_args} {
14
+ nodes {
15
+ #{EPlat::Shopify::Product::Variant.graphql_fields}
16
+ }
17
+ pageInfo {
18
+ endCursor
19
+ startCursor
20
+ hasNextPage
21
+ hasPreviousPage
22
+ }
23
+ }
24
+ }
25
+ GRAPHQL
26
+ end
27
+ end
28
+
29
+ def self.variant
30
+ ->(args) do
31
+ formatted_args = args.present? ? "(#{args})" : ""
32
+
33
+ <<~GRAPHQL
34
+ query getProductVariant {
35
+ productVariant#{formatted_args} {
36
+ #{EPlat::Shopify::Product::Variant.graphql_fields}
37
+ }
38
+ }
39
+ GRAPHQL
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,58 @@
1
+
2
+ module EPlat::Shopify::GraphQL::V202604::Query::Product
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ def self.products
8
+ ->(args) do
9
+ formatted_args = args.present? ? "(#{args})" : ""
10
+
11
+ <<~GRAPHQL
12
+ query getProducts {
13
+ products#{formatted_args} {
14
+ nodes {
15
+ #{EPlat::Shopify::Product.graphql_fields}
16
+ }
17
+ pageInfo {
18
+ endCursor
19
+ startCursor
20
+ hasNextPage
21
+ hasPreviousPage
22
+ }
23
+ }
24
+ }
25
+ GRAPHQL
26
+ end
27
+ end
28
+
29
+ def self.product
30
+ ->(args) do
31
+ formatted_args = args.present? ? "(#{args})" : ""
32
+
33
+ <<~GRAPHQL
34
+ query getProduct {
35
+ product#{formatted_args} {
36
+ #{EPlat::Shopify::Product.graphql_fields}
37
+ }
38
+ }
39
+ GRAPHQL
40
+ end
41
+ end
42
+
43
+ def self.products_count
44
+ ->(args) do
45
+ formatted_args = args.present? ? "(#{args})" : ""
46
+
47
+ <<~GRAPHQL
48
+ query productsCount {
49
+ productsCount#{formatted_args} {
50
+ count
51
+ }
52
+ }
53
+ GRAPHQL
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ class EPlat::Shopify::GraphQL::V202604::Query
2
+ include Product, Product::Variant
3
+ end
4
+
5
+
@@ -1,3 +1,3 @@
1
1
  module EPlat
2
- VERSION = "1.2.0"
2
+ VERSION = "1.4.0"
3
3
  end