shopify_api 14.4.0 → 14.5.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/Gemfile.lock +1 -1
  4. data/docs/usage/custom_apps.md +32 -1
  5. data/docs/usage/webhooks.md +3 -2
  6. data/lib/shopify_api/clients/graphql/client.rb +12 -2
  7. data/lib/shopify_api/clients/graphql/storefront.rb +10 -2
  8. data/lib/shopify_api/rest/base.rb +7 -1
  9. data/lib/shopify_api/rest/resources/2022_04/customer.rb +1 -0
  10. data/lib/shopify_api/rest/resources/2022_04/recurring_application_charge.rb +9 -0
  11. data/lib/shopify_api/rest/resources/2022_04/shop.rb +10 -0
  12. data/lib/shopify_api/rest/resources/2022_07/customer.rb +1 -0
  13. data/lib/shopify_api/rest/resources/2022_07/recurring_application_charge.rb +9 -0
  14. data/lib/shopify_api/rest/resources/2022_07/shop.rb +10 -0
  15. data/lib/shopify_api/rest/resources/2022_10/customer.rb +1 -0
  16. data/lib/shopify_api/rest/resources/2022_10/recurring_application_charge.rb +9 -0
  17. data/lib/shopify_api/rest/resources/2022_10/shop.rb +10 -0
  18. data/lib/shopify_api/rest/resources/2023_01/customer.rb +1 -0
  19. data/lib/shopify_api/rest/resources/2023_01/recurring_application_charge.rb +9 -0
  20. data/lib/shopify_api/rest/resources/2023_01/shop.rb +10 -0
  21. data/lib/shopify_api/rest/resources/2023_04/customer.rb +1 -0
  22. data/lib/shopify_api/rest/resources/2023_04/recurring_application_charge.rb +9 -0
  23. data/lib/shopify_api/rest/resources/2023_04/shop.rb +10 -0
  24. data/lib/shopify_api/rest/resources/2023_07/customer.rb +1 -0
  25. data/lib/shopify_api/rest/resources/2023_07/recurring_application_charge.rb +9 -0
  26. data/lib/shopify_api/rest/resources/2023_07/shop.rb +10 -0
  27. data/lib/shopify_api/rest/resources/2023_10/customer.rb +1 -0
  28. data/lib/shopify_api/rest/resources/2023_10/recurring_application_charge.rb +9 -0
  29. data/lib/shopify_api/rest/resources/2023_10/shop.rb +10 -0
  30. data/lib/shopify_api/rest/resources/2024_01/customer.rb +1 -0
  31. data/lib/shopify_api/rest/resources/2024_01/recurring_application_charge.rb +9 -0
  32. data/lib/shopify_api/rest/resources/2024_01/shop.rb +10 -0
  33. data/lib/shopify_api/rest/resources/2024_04/customer.rb +1 -0
  34. data/lib/shopify_api/rest/resources/2024_04/recurring_application_charge.rb +9 -0
  35. data/lib/shopify_api/rest/resources/2024_04/shop.rb +10 -0
  36. data/lib/shopify_api/rest/resources/2024_07/customer.rb +2 -0
  37. data/lib/shopify_api/rest/resources/2024_07/recurring_application_charge.rb +8 -0
  38. data/lib/shopify_api/rest/resources/2024_07/shop.rb +10 -0
  39. data/lib/shopify_api/utils/attributes_comparator.rb +21 -4
  40. data/lib/shopify_api/version.rb +1 -1
  41. data/sorbet/rbi/shims/hash.rb +3 -0
  42. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc6cb9b900f3eba7d192c4d5091308edeaff8245517254af586192f72f57417c
4
- data.tar.gz: bb86d0654b8aa58ce70c017707ac700cb51094467608119d75d2235942483d85
3
+ metadata.gz: 8b9b61bc6316148fcd2d905384def067e31b4fca5265bb1327a7502020ee6104
4
+ data.tar.gz: b3971fadf5e0159a68953fee0883be0a6fb5f366d0a25f39201cc6c134caa0bb
5
5
  SHA512:
6
- metadata.gz: d7a256c3ce166b787b5a21d228fe25d73fc2ea1f0bbe2fca6896c3c07bae3300336c1c2efc7943d7a0096cdfd99bd0938d81fe1c097a39b2ef2f9ee931edbea2
7
- data.tar.gz: 42c03e72834f764f6435c7f81286ef45502a36819ce4647eb6a346eb48e349d6f6f59dd5913bef591d4a45ae95e339d6bea9c016109d9d6bec0c077fc86bf29c
6
+ metadata.gz: 845bcdfac189ab0b17a07540c7cad3ba3621bbc83622d16b32a143f37eaaf9909499beb8791a2f0a0b6726e8283f833c5f1702ae804bbe38393ee5d6fa0fef67
7
+ data.tar.gz: f0216976a29a1b59ab8bf89316d3ccc9b3cbda4a42b81490cb5e25af19d9cf2b2b196484397816decdde75065f387e1f9bfddd5d82a42c110be8f13654c6f8d9
data/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 14.5.0
8
+
9
+ - [#1327](https://github.com/Shopify/shopify-api-ruby/pull/1327) Support `?debug=true` parameter in GraphQL client requests
10
+ - [#1308](https://github.com/Shopify/shopify-api-ruby/pull/1308) Support hash_with_indifferent_access when creating REST objects from Shopify responses. Closes #1296
11
+ - [#1332](https://github.com/Shopify/shopify-api-ruby/pull/1332) Fixed an issue where `Customer` REST API PUT requests didn't send all of the fields in the `email_marketing_consent` attribute
12
+ - [#1335](https://github.com/Shopify/shopify-api-ruby/pull/1335) Add back the `current` methods for `Shop` and `RecurringApplicationCharge` resources
13
+
7
14
  ## 14.4.0
8
15
 
9
16
  - [#1325](https://github.com/Shopify/shopify-api-ruby/pull/1325) Add support for 2024-07 API version
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify_api (14.4.0)
4
+ shopify_api (14.5.0)
5
5
  activesupport
6
6
  concurrent-ruby
7
7
  hash_diff
@@ -53,6 +53,15 @@ def configure_app
53
53
  access_token: "the_token_for_your_custom_app_found_in_admin"
54
54
  )
55
55
 
56
+ ShopifyAPI::Context.setup(
57
+ api_key: "<api-key>",
58
+ api_secret_key: "<api-secret-key>",
59
+ scope: "read_orders,read_products,etc",
60
+ is_embedded: true, # Set to true if you are building an embedded app
61
+ api_version: "2024-01", # The version of the API you would like to use
62
+ is_private: true, # Set to true if you have an existing private app
63
+ )
64
+
56
65
  # Activate session to be used in all API calls
57
66
  # session must be type `ShopifyAPI::Auth::Session`
58
67
  ShopifyAPI::Context.activate_session(session)
@@ -62,9 +71,31 @@ end
62
71
  def make_api_request
63
72
  # 1. Create API client without session information
64
73
  # The graphql_client will use `ShopifyAPI::Context.active_session` when making API calls
65
- graphql_client = ShopifyAPI::Clients::Graphql::Admin.new
74
+ # you can set the api version for your GraphQL client to override the api version in ShopifyAPI::Context
75
+ graphql_client = ShopifyAPI::Clients::Graphql::Admin.new(api_version: "2024-07")
66
76
 
67
77
  # 2. Use API client to make queries
78
+ # Graphql
79
+ query = <<~QUERY
80
+ {
81
+ products(first: 10) {
82
+ edges {
83
+ cursor
84
+ node {
85
+ id
86
+ title
87
+ onlineStoreUrl
88
+ }
89
+ }
90
+ }
91
+ }
92
+ QUERY
93
+
94
+ response = graphql_client.query(query: query)
95
+
96
+ # Use REST resources to make authenticated API call
97
+ product_count = ShopifyAPI::Product.count
98
+
68
99
  ...
69
100
  end
70
101
 
@@ -21,8 +21,9 @@ module WebhookHandler
21
21
  extend ShopifyAPI::Webhooks::WebhookHandler
22
22
 
23
23
  class << self
24
- def handle_webhook(data:)
25
- puts "Received webhook! topic: #{data.topic} shop: #{data.shop} body: #{data.body} webhook_id: #{data.webhook_id} api_version: #{data.api_version"
24
+ def handle(data:)
25
+ puts "Received webhook! topic: #{data.topic} shop: #{data.shop} body: #{data.body} webhook_id: #{data.webhook_id} api_version: #{data.api_version}"
26
+ perform_later(topic: data.topic, shop_domain: data.shop, webhook: data.body)
26
27
  end
27
28
  end
28
29
  end
@@ -29,14 +29,24 @@ module ShopifyAPI
29
29
  headers: T.nilable(T::Hash[T.any(Symbol, String), T.untyped]),
30
30
  tries: Integer,
31
31
  response_as_struct: T.nilable(T::Boolean),
32
+ debug: T::Boolean,
32
33
  ).returns(HttpResponse)
33
34
  end
34
- def query(query:, variables: nil, headers: nil, tries: 1, response_as_struct: Context.response_as_struct)
35
+ def query(
36
+ query:,
37
+ variables: nil,
38
+ headers: nil,
39
+ tries: 1,
40
+ response_as_struct: Context.response_as_struct,
41
+ debug: false
42
+ )
35
43
  body = { query: query, variables: variables }
44
+ search_params = debug ? "?debug=true" : ""
45
+
36
46
  @http_client.request(
37
47
  HttpRequest.new(
38
48
  http_method: :post,
39
- path: "#{@api_version}/graphql.json",
49
+ path: "#{@api_version}/graphql.json#{search_params}",
40
50
  body: body,
41
51
  query: nil,
42
52
  extra_headers: headers,
@@ -46,12 +46,20 @@ module ShopifyAPI
46
46
  headers: T.nilable(T::Hash[T.any(Symbol, String), T.untyped]),
47
47
  tries: Integer,
48
48
  response_as_struct: T.nilable(T::Boolean),
49
+ debug: T::Boolean,
49
50
  ).returns(HttpResponse)
50
51
  end
51
- def query(query:, variables: nil, headers: {}, tries: 1, response_as_struct: Context.response_as_struct)
52
+ def query(
53
+ query:,
54
+ variables: nil,
55
+ headers: {},
56
+ tries: 1,
57
+ response_as_struct: Context.response_as_struct,
58
+ debug: false
59
+ )
52
60
  T.must(headers).merge!({ @storefront_auth_header => @storefront_access_token })
53
61
  super(query: query, variables: variables, headers: headers, tries: tries,
54
- response_as_struct: response_as_struct)
62
+ response_as_struct: response_as_struct, debug: debug)
55
63
  end
56
64
  end
57
65
  end
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "active_support/inflector"
5
+ require "active_support/core_ext/hash/indifferent_access"
5
6
 
6
7
  module ShopifyAPI
7
8
  module Rest
@@ -16,6 +17,7 @@ module ShopifyAPI
16
17
  @paths = T.let([], T::Array[T::Hash[Symbol, T.any(T::Array[Symbol], String, Symbol)]])
17
18
  @custom_prefix = T.let(nil, T.nilable(String))
18
19
  @read_only_attributes = T.let([], T.nilable(T::Array[Symbol]))
20
+ @atomic_hash_attributes = T.let([], T::Array[Symbol])
19
21
  @aliased_properties = T.let({}, T::Hash[String, String])
20
22
 
21
23
  sig { returns(T::Hash[Symbol, T.untyped]) }
@@ -61,6 +63,9 @@ module ShopifyAPI
61
63
  sig { returns(T::Hash[Symbol, T::Class[T.anything]]) }
62
64
  attr_reader :has_one
63
65
 
66
+ sig { returns(T.nilable(T::Array[Symbol])) }
67
+ attr_reader :atomic_hash_attributes
68
+
64
69
  sig { returns(T.nilable(T::Hash[T.any(Symbol, String), String])) }
65
70
  attr_accessor :headers
66
71
 
@@ -248,7 +253,7 @@ module ShopifyAPI
248
253
  def create_instances_from_response(response:, session:)
249
254
  objects = []
250
255
 
251
- body = T.cast(response.body, T::Hash[String, T.untyped])
256
+ body = T.cast(response.body, T::Hash[String, T.untyped]).with_indifferent_access
252
257
 
253
258
  response_names = json_response_body_names
254
259
 
@@ -417,6 +422,7 @@ module ShopifyAPI
417
422
  ShopifyAPI::Utils::AttributesComparator.compare(
418
423
  stringified_updatable_attributes,
419
424
  stringified_new_attributes,
425
+ atomic_hash_attributes: self.class.atomic_hash_attributes || [],
420
426
  )
421
427
  end
422
428
 
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -55,6 +55,7 @@ module ShopifyAPI
55
55
  metafield: Metafield
56
56
  }, T::Hash[Symbol, Class])
57
57
  @has_many = T.let({}, T::Hash[Symbol, Class])
58
+ @atomic_hash_attributes = [:email_marketing_consent]
58
59
  @paths = T.let([
59
60
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
61
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,15 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
160
+
152
161
  end
153
162
 
154
163
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -22,6 +22,7 @@ module ShopifyAPI
22
22
  @accepts_marketing_updated_at = T.let(nil, T.nilable(String))
23
23
  @addresses = T.let(nil, T.nilable(T::Array[T.untyped]))
24
24
  @created_at = T.let(nil, T.nilable(String))
25
+
25
26
  @currency = T.let(nil, T.nilable(String))
26
27
  @default_address = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
27
28
  @email = T.let(nil, T.nilable(String))
@@ -55,6 +56,7 @@ module ShopifyAPI
55
56
  metafield: Metafield
56
57
  }, T::Hash[Symbol, Class])
57
58
  @has_many = T.let({}, T::Hash[Symbol, Class])
59
+ @atomic_hash_attributes = [:email_marketing_consent]
58
60
  @paths = T.let([
59
61
  {http_method: :delete, operation: :delete, ids: [:id], path: "customers/<id>.json"},
60
62
  {http_method: :get, operation: :count, ids: [], path: "customers/count.json"},
@@ -149,6 +149,14 @@ module ShopifyAPI
149
149
  T.cast(response, T::Array[RecurringApplicationCharge])
150
150
  end
151
151
 
152
+ sig do
153
+ params(session: Auth::Session)
154
+ .returns(T.nilable(RecurringApplicationCharge))
155
+ end
156
+ def current(session: ShopifyAPI::Context.active_session)
157
+ charges = all(session: session)
158
+ charges.select { |charge| charge.status == "active" }.first
159
+ end
152
160
  end
153
161
 
154
162
  sig do
@@ -216,6 +216,16 @@ module ShopifyAPI
216
216
  T.cast(response, T::Array[Shop])
217
217
  end
218
218
 
219
+ sig do
220
+ params(
221
+ fields: T.untyped,
222
+ session: Auth::Session,
223
+ ).returns(T.nilable(Shop))
224
+ end
225
+ def current(fields: nil, session: ShopifyAPI::Context.active_session)
226
+ all(session: session, fields: fields).first
227
+ end
228
+
219
229
  end
220
230
 
221
231
  end
@@ -13,9 +13,10 @@ module ShopifyAPI
13
13
  params(
14
14
  original_attributes: T::Hash[String, T.untyped],
15
15
  updated_attributes: T::Hash[String, T.untyped],
16
+ atomic_hash_attributes: T::Array[Symbol],
16
17
  ).returns(T::Hash[String, T.untyped])
17
18
  end
18
- def compare(original_attributes, updated_attributes)
19
+ def compare(original_attributes, updated_attributes, atomic_hash_attributes: [])
19
20
  attributes_diff = HashDiff::Comparison.new(
20
21
  original_attributes,
21
22
  updated_attributes,
@@ -24,6 +25,7 @@ module ShopifyAPI
24
25
  update_value = build_update_value(
25
26
  attributes_diff,
26
27
  reference_values: updated_attributes,
28
+ atomic_hash_attributes: atomic_hash_attributes,
27
29
  )
28
30
 
29
31
  update_value
@@ -34,9 +36,10 @@ module ShopifyAPI
34
36
  diff: T::Hash[String, T.untyped],
35
37
  path: T::Array[String],
36
38
  reference_values: T::Hash[String, T.untyped],
39
+ atomic_hash_attributes: T::Array[Symbol],
37
40
  ).returns(T::Hash[String, T.untyped])
38
41
  end
39
- def build_update_value(diff, path: [], reference_values: {})
42
+ def build_update_value(diff, path: [], reference_values: {}, atomic_hash_attributes: [])
40
43
  new_hash = {}
41
44
 
42
45
  diff.each do |key, value|
@@ -49,7 +52,19 @@ module ShopifyAPI
49
52
  if has_numbered_key && ref_value.is_a?(Array)
50
53
  new_hash[key] = ref_value
51
54
  else
52
- new_value = build_update_value(value, path: current_path, reference_values: reference_values)
55
+ new_value = build_update_value(
56
+ value,
57
+ path: current_path,
58
+ reference_values: reference_values,
59
+ atomic_hash_attributes: atomic_hash_attributes,
60
+ )
61
+
62
+ atomic_update = atomic_hash_attributes.include?(key.to_sym)
63
+
64
+ # If the key is in atomic_hash_attributes, we use the entire reference value
65
+ # so we update the hash as a whole.
66
+ if !new_value.empty? && !ref_value.empty? && atomic_update
67
+ new_hash[key] = ref_value
53
68
 
54
69
  # Only add to new_hash if the user intentionally updates
55
70
  # to empty value like `{}` or `[]`. For example:
@@ -70,7 +85,9 @@ module ShopifyAPI
70
85
  # new_hash = {}
71
86
  #
72
87
  # new_hash is empty because nothing changes
73
- new_hash[key] = new_value if !new_value.empty? || ref_value.empty?
88
+ elsif !new_value.empty? || ref_value.empty?
89
+ new_hash[key] = new_value
90
+ end
74
91
  end
75
92
  elsif value != HashDiff::NO_VALUE
76
93
  new_hash[key] = value
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ShopifyAPI
5
- VERSION = "14.4.0"
5
+ VERSION = "14.5.0"
6
6
  end
@@ -0,0 +1,3 @@
1
+ class Hash
2
+ def with_indifferent_access; end
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 14.4.0
4
+ version: 14.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-02 00:00:00.000000000 Z
11
+ date: 2024-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -1058,6 +1058,7 @@ files:
1058
1058
  - sorbet/rbi/gems/yard@0.9.27.rbi
1059
1059
  - sorbet/rbi/gems/zeitwerk@2.5.4.rbi
1060
1060
  - sorbet/rbi/shims/fakefs.rbi
1061
+ - sorbet/rbi/shims/hash.rb
1061
1062
  - sorbet/rbi/shims/openssl.rb
1062
1063
  - sorbet/rbi/todo.rbi
1063
1064
  - sorbet/tapioca/config.yml
@@ -1083,7 +1084,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1083
1084
  - !ruby/object:Gem::Version
1084
1085
  version: '0'
1085
1086
  requirements: []
1086
- rubygems_version: 3.5.14
1087
+ rubygems_version: 3.5.17
1087
1088
  signing_key:
1088
1089
  specification_version: 4
1089
1090
  summary: The gem for accessing the Shopify API