shopify_graphql 1.2.6 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a20f7b85f9a4a22fd1c3ea0509ec496f3447a2764fedf105539cab20274ed221
4
- data.tar.gz: 966022527dd821e837c200d6c1df8ca84236b05ed5e311c6ac13dbabe00322f9
3
+ metadata.gz: fe9ea3cbb7c46d86fe1281d9658cc1de370db1b306b29aa331c1c8ac9ae94f0d
4
+ data.tar.gz: ab91936f744cd3c40ee1b81952426a839e26fb3fd8b9ca55defa5248c22ed812
5
5
  SHA512:
6
- metadata.gz: 02c0027937db4125f3a1146275a999e1c6657ebe28682d5e0da81d34fcebac78796877abc4e7d1572de84599a82033d2140cabd8afbdd9b39951e428a9d4644f
7
- data.tar.gz: 4eeb9be4a243c5bcba638857a6bfa29f28e20a94ba1d3aa236ac30fdf7a2a6587bd4d74a6b7a53349118a60072458fc87fc255c16560b76b07c0d64c33e2ae13
6
+ metadata.gz: 2ef56f4a0248e446d3063fd6ef92e13743d65a6ecefd523c8b70444f52353667e9b082efb13177a5912ad598425c6382e0ac36321aab231b46f73871e1109a06
7
+ data.tar.gz: 3c483532a0d6dc2d853a172a357a07319466bad2313937a1f57f4f359a2978affeacb80f3e3b5c3df287bc2e07565d692e831dff7564a22cc9a02a1522bab59f
data/README.md CHANGED
@@ -676,12 +676,33 @@ ShopifyGraphql.handle_user_errors(response)
676
676
 
677
677
  ## Built-in Graphql calls
678
678
 
679
+ - `ShopifyGraphql::CurrentShop`:
680
+
681
+ Equivalent to `ShopifyAPI::Shop.current`. Usage example:
682
+
683
+ ```rb
684
+ shop = ShopifyGraphql::CurrentShop.call
685
+ puts shop.name
686
+ ```
687
+
688
+ Or with locales (requires `read_locales` scope):
689
+
690
+ ```rb
691
+ shop = ShopifyGraphql::CurrentShop.call(with_locales: true)
692
+ puts shop.primary_locale
693
+ puts shop.shop_locales
694
+ ```
695
+
679
696
  - `ShopifyGraphql::CancelSubscription`
680
697
  - `ShopifyGraphql::CreateRecurringSubscription`
681
698
  - `ShopifyGraphql::CreateUsageSubscription`
682
699
  - `ShopifyGraphql::GetAppSubscription`
683
700
  - `ShopifyGraphql::UpsertPrivateMetafield`
684
701
  - `ShopifyGraphql::DeletePrivateMetafield`
702
+ - `ShopifyGraphql::CreateBulkMutation`
703
+ - `ShopifyGraphql::CreateBulkQuery`
704
+ - `ShopifyGraphql::CreateStagedUploads`
705
+ - `ShopifyGraphql::GetBulkOperation`
685
706
 
686
707
  Built-in wrappers are located in [`app/graphql/shopify_graphql`](/app/graphql/shopify_graphql/) folder. You can use them directly in your apps or as an example to create your own wrappers.
687
708
 
@@ -709,9 +730,73 @@ response.query_cost # => 1
709
730
  response.points_maxed?(threshold: 100) # => false
710
731
  ```
711
732
 
712
- ## Graphql webhooks
733
+ ## Custom apps
734
+
735
+ In custom apps, if you're using `shopify_app` gem, then the setup is similar public apps. Except `Shop` model which must include class method to make queries to your store:
736
+
737
+ ```rb
738
+ # app/models/shop.rb
739
+ class Shop < ActiveRecord::Base
740
+ include ShopifyApp::ShopSessionStorageWithScopes
741
+
742
+ def self.system
743
+ new(
744
+ shopify_domain: "MYSHOPIFY_DOMAIN",
745
+ shopify_token: "API_ACCESS_TOKEN_FOR_CUSTOM_APP"
746
+ )
747
+ end
748
+ end
749
+ ```
750
+
751
+ Using this method, you should be able to make API calls like this:
752
+
753
+ ```rb
754
+ Shop.system.with_shopify_session do
755
+ GetOrder.call(id: order.shopify_gid)
756
+ end
757
+ ```
758
+
759
+ If you're not using `shopify_app` gem, then you need to setup `ShopifyAPI::Context` manually:
760
+
761
+ ```rb
762
+ # config/initializers/shopify_api.rb
763
+ ShopifyAPI::Context.setup(
764
+ api_key: "XXX",
765
+ api_secret_key: "XXXX",
766
+ scope: "read_orders,read_products",
767
+ is_embedded: false,
768
+ api_version: "2024-07",
769
+ is_private: true,
770
+ )
771
+ ```
772
+
773
+ And create another method in Shop model to make queries to your store:
774
+
775
+ ```rb
776
+ # app/models/shop.rb
777
+ def Shop
778
+ def self.with_shopify_session(&block)
779
+ ShopifyAPI::Auth::Session.temp(
780
+ shop: "MYSHOPIFY_DOMAIN",
781
+ access_token: "API_ACCESS_TOKEN_FOR_CUSTOM_APP",
782
+ &block
783
+ )
784
+ end
785
+ end
786
+ ```
787
+
788
+ Using this method, you should be able to make API calls like this:
789
+
790
+ ```rb
791
+ Shop.with_shopify_session do
792
+ GetOrder.call(id: order.shopify_gid)
793
+ end
794
+ ```
795
+
796
+ ## Graphql webhooks (deprecated)
713
797
 
714
- > Since version 10 `shopify_api` gem includes built-in support for Graphql webhooks. If you are using `shopify_api` version 10 or higher you don't need to use this gem to handle Graphql webhooks. See [`shopify_app` documentation](https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/webhooks.md) for more details.
798
+ > [!WARNING]
799
+ > ShopifyGraphql webhooks are deprecated and will be removed in v3.0. Please use `shopify_app` gem for handling webhooks. See [`shopify_app` documentation](https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/webhooks.md) for more details.
715
800
 
716
801
  The gem has built-in support for Graphql webhooks (similar to `shopify_app`). To enable it add the following config to `config/initializers/shopify_app.rb`:
717
802
 
@@ -0,0 +1,27 @@
1
+ module ShopifyGraphql
2
+ class CreateBulkMutation
3
+ include Mutation
4
+
5
+ MUTATION = <<~GRAPHQL
6
+ mutation($mutation: String!, $stagedUploadPath: String!) {
7
+ bulkOperationRunMutation(mutation: $mutation, stagedUploadPath: $stagedUploadPath) {
8
+ bulkOperation {
9
+ id
10
+ status
11
+ }
12
+ userErrors {
13
+ field
14
+ message
15
+ }
16
+ }
17
+ }
18
+ GRAPHQL
19
+
20
+ def call(mutation:, staged_upload_path:)
21
+ response = execute(MUTATION, mutation: mutation, stagedUploadPath: staged_upload_path)
22
+ response.data = response.data.bulkOperationRunMutation
23
+ handle_user_errors(response.data)
24
+ response
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module ShopifyGraphql
2
+ class CreateBulkQuery
3
+ include Mutation
4
+
5
+ MUTATION = <<~GRAPHQL
6
+ mutation($query: String!) {
7
+ bulkOperationRunQuery(query: $query) {
8
+ bulkOperation {
9
+ id
10
+ status
11
+ }
12
+ userErrors {
13
+ field
14
+ message
15
+ }
16
+ }
17
+ }
18
+ GRAPHQL
19
+
20
+ def call(query:)
21
+ response = execute(MUTATION, query: query)
22
+ response.data = response.data.bulkOperationRunQuery
23
+ handle_user_errors(response.data)
24
+ response
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module ShopifyGraphql
2
+ class CreateStagedUploads
3
+ include Mutation
4
+
5
+ MUTATION = <<~GRAPHQL
6
+ mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
7
+ stagedUploadsCreate(input: $input) {
8
+ stagedTargets {
9
+ url
10
+ resourceUrl
11
+ parameters {
12
+ name
13
+ value
14
+ }
15
+ }
16
+ userErrors{
17
+ field,
18
+ message
19
+ }
20
+ }
21
+ }
22
+ GRAPHQL
23
+
24
+ def call(input:)
25
+ response = execute(MUTATION, input: input)
26
+ response.data = response.data.stagedUploadsCreate
27
+ handle_user_errors(response.data)
28
+ response
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,139 @@
1
+ module ShopifyGraphql
2
+ class CurrentShop
3
+ include ShopifyGraphql::Query
4
+
5
+ QUERY = <<~GRAPHQL
6
+ query {
7
+ shop {
8
+ id
9
+ name
10
+ email
11
+ contactEmail
12
+ myshopifyDomain
13
+ primaryDomain {
14
+ host
15
+ }
16
+ #CREATED_AT#
17
+ #UPDATED_AT#
18
+ #SHOP_OWNER_NAME#
19
+ currencyCode
20
+ billingAddress {
21
+ country
22
+ countryCodeV2
23
+ province
24
+ city
25
+ address1
26
+ address2
27
+ zip
28
+ latitude
29
+ longitude
30
+ phone
31
+ }
32
+ timezoneAbbreviation
33
+ ianaTimezone
34
+ plan {
35
+ displayName
36
+ }
37
+ currencyFormats {
38
+ moneyFormat
39
+ moneyInEmailsFormat
40
+ moneyWithCurrencyFormat
41
+ moneyWithCurrencyInEmailsFormat
42
+ }
43
+ weightUnit
44
+ taxShipping
45
+ taxesIncluded
46
+ setupRequired
47
+ checkoutApiSupported
48
+ transactionalSmsDisabled
49
+ enabledPresentmentCurrencies
50
+ #SMS_CONSENT#
51
+ }
52
+ #LOCALES_SUBQUERY#
53
+ }
54
+ GRAPHQL
55
+ LOCALES_SUBQUERY = <<~GRAPHQL
56
+ shopLocales {
57
+ locale
58
+ primary
59
+ }
60
+ GRAPHQL
61
+
62
+ def call(with_locales: false)
63
+ query = prepare_query(QUERY, with_locales: with_locales)
64
+ response = execute(query)
65
+ parse_data(response.data, with_locales: with_locales)
66
+ end
67
+
68
+ private
69
+
70
+ def prepare_query(query, with_locales:)
71
+ query = query.gsub("#LOCALES_SUBQUERY#", with_locales ? LOCALES_SUBQUERY : "")
72
+ if ShopifyAPI::Context.api_version.in?(%w[2024-01 2024-04 2024-07])
73
+ query.gsub!("#SHOP_OWNER_NAME#", "")
74
+ else
75
+ query.gsub!("#SHOP_OWNER_NAME#", "shopOwnerName")
76
+ end
77
+ if ShopifyAPI::Context.api_version.in?(%w[2024-01])
78
+ query.gsub!("#CREATED_AT#", "")
79
+ query.gsub!("#UPDATED_AT#", "")
80
+ query.gsub!("#SMS_CONSENT#", "")
81
+ else
82
+ query.gsub!("#CREATED_AT#", "createdAt")
83
+ query.gsub!("#UPDATED_AT#", "updatedAt")
84
+ query.gsub!("#SMS_CONSENT#", "marketingSmsConsentEnabledAtCheckout")
85
+ end
86
+ query
87
+ end
88
+
89
+ def parse_data(data, with_locales: false)
90
+ plan_display_name = ShopifyGraphql.normalize_plan_display_name(data.shop.plan.displayName)
91
+ plan_name = ShopifyGraphql::DISPLAY_NAME_TO_PLAN[plan_display_name]
92
+ response = OpenStruct.new(
93
+ id: data.shop.id.split("/").last.to_i,
94
+ name: data.shop.name,
95
+ email: data.shop.email,
96
+ customer_email: data.shop.contactEmail,
97
+ myshopify_domain: data.shop.myshopifyDomain,
98
+ domain: data.shop.primaryDomain.host,
99
+ created_at: data.shop.createdAt,
100
+ updated_at: data.shop.updatedAt,
101
+ shop_owner: data.shop.shopOwnerName,
102
+ currency: data.shop.currencyCode,
103
+ country_name: data.shop.billingAddress.country,
104
+ country: data.shop.billingAddress.countryCodeV2,
105
+ country_code: data.shop.billingAddress.countryCodeV2,
106
+ province: data.shop.billingAddress.province,
107
+ province_code: data.shop.billingAddress.provinceCode,
108
+ city: data.shop.billingAddress.city,
109
+ address1: data.shop.billingAddress.address1,
110
+ address2: data.shop.billingAddress.address2,
111
+ zip: data.shop.billingAddress.zip,
112
+ latitude: data.shop.billingAddress.latitude,
113
+ longitude: data.shop.billingAddress.longitude,
114
+ phone: data.shop.billingAddress.phone,
115
+ timezone: data.shop.timezoneAbbreviation,
116
+ iana_timezone: data.shop.ianaTimezone,
117
+ plan_name: plan_name,
118
+ plan_display_name: plan_display_name,
119
+ money_format: data.shop.currencyFormats.moneyFormat,
120
+ money_in_emails_format: data.shop.currencyFormats.moneyInEmailsFormat,
121
+ money_with_currency_format: data.shop.currencyFormats.moneyWithCurrencyFormat,
122
+ money_with_currency_in_emails_format: data.shop.currencyFormats.moneyWithCurrencyInEmailsFormat,
123
+ weight_unit: data.shop.weightUnit,
124
+ tax_shipping: data.shop.taxShipping,
125
+ taxes_included: data.shop.taxesIncluded,
126
+ setup_required: data.shop.setupRequired,
127
+ checkout_api_supported: data.shop.checkoutApiSupported,
128
+ transactional_sms_disabled: data.shop.transactionalSmsDisabled,
129
+ enabled_presentment_currencies: data.shop.enabledPresentmentCurrencies,
130
+ marketing_sms_consent_enabled_at_checkout: data.shop.marketingSmsConsentEnabledAtCheckout
131
+ )
132
+ if with_locales
133
+ response.primary_locale = data.shopLocales.find(&:primary).locale
134
+ response.shop_locales = data.shopLocales.map(&:locale)
135
+ end
136
+ response
137
+ end
138
+ end
139
+ end
@@ -22,7 +22,7 @@ module ShopifyGraphql
22
22
 
23
23
  def parse_data(data)
24
24
  unless data.node
25
- raise ResourceNotFound.new(200, "Subscription not found")
25
+ raise ResourceNotFound.new, "Subscription not found"
26
26
  end
27
27
 
28
28
  OpenStruct.new(
@@ -0,0 +1,44 @@
1
+ module ShopifyGraphql
2
+ class GetBulkOperation
3
+ include Query
4
+
5
+ QUERY = <<~GRAPHQL
6
+ query($id: ID!){
7
+ node(id: $id) {
8
+ ... on BulkOperation {
9
+ id
10
+ status
11
+ errorCode
12
+ createdAt
13
+ completedAt
14
+ fileSize
15
+ url
16
+ }
17
+ }
18
+ }
19
+ GRAPHQL
20
+
21
+ def call(id:)
22
+ response = execute(QUERY, id: id)
23
+ response.data = parse_data(response.data.node)
24
+ response
25
+ end
26
+
27
+ private
28
+
29
+ def parse_data(data)
30
+ unless data
31
+ raise ResourceNotFound.new, "BulkOperation not found"
32
+ end
33
+
34
+ OpenStruct.new(
35
+ id: data.id,
36
+ status: data.status,
37
+ error_code: data.errorCode,
38
+ created_at: Time.find_zone("UTC").parse(data.createdAt),
39
+ completed_at: data.completedAt ? Time.find_zone("UTC").parse(data.completedAt) : nil,
40
+ url: data.url
41
+ )
42
+ end
43
+ end
44
+ end
@@ -1,4 +1,29 @@
1
1
  module ShopifyGraphql
2
+ # Mapping from deprecated plan_name to plan_display_name
3
+ PLAN_TO_DISPLAY_NAME = {
4
+ "trial" => "trial",
5
+ "frozen" => "frozen",
6
+ "fraudulent" => "cancelled",
7
+ "shopify_alumni" => "shopify_alumni",
8
+ "affiliate" => "development",
9
+ "basic" => "basic",
10
+ "professional" => "shopify",
11
+ "npo_full" => "npo_full",
12
+ "shopify_plus" => "shopify_plus",
13
+ "staff" => "staff",
14
+ "unlimited" => "advanced",
15
+ "retail" => "retail",
16
+ "cancelled" => "cancelled",
17
+ "dormant" => "pause_and_build",
18
+ "starter_2022" => "shopify_starter",
19
+ "plus_partner_sandbox" => "shopify_plus_partner_sandbox",
20
+ "paid_trial" => "extended_trial",
21
+ "partner_test" => "developer_preview",
22
+ "open_learning" => "open_learning",
23
+ "staff_business" => "staff_business"
24
+ }
25
+ DISPLAY_NAME_TO_PLAN = PLAN_TO_DISPLAY_NAME.invert
26
+
2
27
  class Client
3
28
  def client
4
29
  @client ||= ShopifyAPI::Clients::Graphql::Admin.new(session: ShopifyAPI::Context.active_session)
@@ -8,11 +33,17 @@ module ShopifyGraphql
8
33
  response = client.query(query: query, variables: variables)
9
34
  Response.new(handle_response(response))
10
35
  rescue ShopifyAPI::Errors::HttpResponseError => e
11
- Response.new(handle_response(e.response))
36
+ Response.new(handle_response(e.response, e))
12
37
  rescue JSON::ParserError => e
13
- raise ServerError.new(e, "Invalid JSON response")
14
- rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED, Errno::ENETUNREACH, Net::ReadTimeout, Net::OpenTimeout, OpenSSL::SSL::SSLError, EOFError, Socket::ResolutionError => e
15
- raise ServerError.new(e, "Network error")
38
+ raise ServerError.new(response: response), "Invalid JSON response: #{e.message}"
39
+ rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED, Errno::ENETUNREACH, Net::ReadTimeout, Net::OpenTimeout, OpenSSL::SSL::SSLError, EOFError => e
40
+ raise ServerError.new(response: response), "Network error: #{e.message}"
41
+ rescue => e
42
+ if (defined?(Socket::ResolutionError) and e.is_a?(Socket::ResolutionError))
43
+ raise ServerError.new(response: response), "Network error: #{e.message}"
44
+ else
45
+ raise e
46
+ end
16
47
  end
17
48
 
18
49
  def parsed_body(response)
@@ -23,78 +54,107 @@ module ShopifyGraphql
23
54
  end
24
55
  end
25
56
 
26
- def handle_response(response)
57
+ def handle_response(response, error = nil)
27
58
  case response.code
28
59
  when 200..400
29
- handle_graphql_errors(parsed_body(response))
60
+ handle_graphql_errors(response)
30
61
  when 400
31
- raise BadRequest.new(parsed_body(response), code: response.code)
62
+ raise BadRequest.new(response: response), error.message
32
63
  when 401
33
- raise UnauthorizedAccess.new(parsed_body(response), code: response.code)
64
+ raise UnauthorizedAccess.new(response: response), error.message
34
65
  when 402
35
- raise PaymentRequired.new(parsed_body(response), code: response.code)
66
+ raise PaymentRequired.new(response: response), error.message
36
67
  when 403
37
- raise ForbiddenAccess.new(parsed_body(response), code: response.code)
68
+ raise ForbiddenAccess.new(response: response), error.message
38
69
  when 404
39
- raise ResourceNotFound.new(parsed_body(response), code: response.code)
70
+ raise ResourceNotFound.new(response: response), error.message
40
71
  when 405
41
- raise MethodNotAllowed.new(parsed_body(response), code: response.code)
72
+ raise MethodNotAllowed.new(response: response), error.message
42
73
  when 409
43
- raise ResourceConflict.new(parsed_body(response), code: response.code)
74
+ raise ResourceConflict.new(response: response), error.message
44
75
  when 410
45
- raise ResourceGone.new(parsed_body(response), code: response.code)
76
+ raise ResourceGone.new(response: response), error.message
46
77
  when 412
47
- raise PreconditionFailed.new(parsed_body(response), code: response.code)
78
+ raise PreconditionFailed.new(response: response), error.message
48
79
  when 422
49
- raise ResourceInvalid.new(parsed_body(response), code: response.code)
80
+ raise ResourceInvalid.new(response: response), error.message
50
81
  when 423
51
- raise ShopLocked.new(parsed_body(response), code: response.code)
82
+ raise ShopLocked.new(response: response), error.message
52
83
  when 429, 430
53
- raise TooManyRequests.new(parsed_body(response), code: response.code)
84
+ raise TooManyRequests.new(response: response), error.message
54
85
  when 401...500
55
- raise ClientError.new(parsed_body(response), code: response.code)
86
+ raise ClientError.new(response: response), error.message
56
87
  when 500...600
57
- raise ServerError.new(parsed_body(response), code: response.code)
88
+ raise ServerError.new(response: response), error.message
58
89
  else
59
- raise ConnectionError.new(parsed_body(response), "Unknown response code: #{response.code}")
90
+ raise ConnectionError.new(response: response), error.message
60
91
  end
61
92
  end
62
93
 
63
94
  def handle_graphql_errors(response)
64
- return response if response.errors.blank?
95
+ parsed_body = parsed_body(response)
96
+ if parsed_body.errors.nil? || parsed_body.errors.empty?
97
+ return parsed_body(response)
98
+ end
65
99
 
66
- error = response.errors.first
67
- error_message = error.message
100
+ error = parsed_body.errors.first
68
101
  error_code = error.extensions&.code
69
- error_doc = error.extensions&.documentation
102
+ error_message = generate_error_message(
103
+ message: error.message,
104
+ code: error_code,
105
+ doc: error.extensions&.documentation
106
+ )
70
107
 
71
- case error_code
72
- when "THROTTLED"
73
- raise TooManyRequests.new(response, error_message, code: error_code, doc: error_doc)
74
- when "INTERNAL_SERVER_ERROR"
75
- raise ServerError.new(response, error_message, code: error_code, doc: error_doc)
76
- else
77
- raise ConnectionError.new(response, error_message, code: error_code, doc: error_doc)
78
- end
108
+ exception = case error_code
109
+ when "THROTTLED"
110
+ TooManyRequests.new(response: response)
111
+ when "INTERNAL_SERVER_ERROR"
112
+ ServerError.new(response: response)
113
+ else
114
+ ConnectionError.new(response: response)
115
+ end
116
+ exception.error_code = error_code
117
+ raise exception, error_message
79
118
  end
80
119
 
81
120
  def handle_user_errors(response)
82
121
  return response if response.userErrors.blank?
83
122
 
84
123
  error = response.userErrors.first
85
- error_message = error.message
86
- error_fields = error.field
87
124
  error_code = error.code
125
+ error_message = generate_error_message(
126
+ message: error.message,
127
+ code: error_code,
128
+ fields: error.field,
129
+ )
130
+
131
+ exception = UserError.new(response: response)
132
+ exception.error_code = error_code
133
+ exception.fields = error.field
134
+ raise exception, error_message
135
+ end
88
136
 
89
- raise UserError.new(response, error_message, fields: error_fields, code: error_code)
137
+ def generate_error_message(message: nil, code: nil, doc: nil, fields: nil)
138
+ string = "Failed.".dup
139
+ string << " Response code = #{code}." if code
140
+ string << " Response message = #{message}.".gsub("..", ".") if message
141
+ string << " Documentation = #{doc}." if doc
142
+ string << " Fields = #{fields}." if fields
143
+ string
90
144
  end
91
145
  end
92
146
 
93
147
  class << self
94
- delegate :execute, :handle_user_errors, to: :client
148
+ extend Forwardable
149
+ def_delegators :client, :execute, :handle_user_errors
95
150
 
96
151
  def client
97
152
  Client.new
98
153
  end
154
+
155
+ def normalize_plan_display_name(plan_display_name)
156
+ return if plan_display_name.blank?
157
+ plan_display_name.parameterize(separator: "_")
158
+ end
99
159
  end
100
160
  end
@@ -4,12 +4,17 @@ module ShopifyGraphql
4
4
  include ShopifyGraphql::PayloadVerification
5
5
 
6
6
  included do
7
+ before_action :deprecate_webhooks
7
8
  skip_before_action :verify_authenticity_token, raise: false
8
9
  before_action :verify_request
9
10
  end
10
11
 
11
12
  private
12
13
 
14
+ def deprecate_webhooks
15
+ ShopifyGraphql.deprecator.warn("ShopifyGraphql webhooks are deprecated and will be removed in v3.0. Please use shopify_app gem for handling webhooks.")
16
+ end
17
+
13
18
  def verify_request
14
19
  data = request.raw_post
15
20
  return head(:unauthorized) unless hmac_valid?(data)
@@ -1,4 +1,8 @@
1
1
  module ShopifyGraphql
2
+ def self.deprecator
3
+ @deprecator ||= ActiveSupport::Deprecation.new("3.0", "ShopifyGraphql")
4
+ end
5
+
2
6
  module RedactJobParams
3
7
  private
4
8
 
@@ -1,22 +1,23 @@
1
1
  module ShopifyGraphql
2
- class ConnectionError < StandardError
3
- attr_reader :response, :code, :doc, :fields
4
-
5
- def initialize(response, message = nil, code: nil, doc: nil, fields: nil)
6
- @response = response
7
- @message = message
8
- @code = code
9
- @doc = doc
10
- @fields = fields
11
- end
12
-
13
- def to_s
14
- message = "Failed.".dup
15
- message << " Response code = #{@code}." if @code
16
- message << " Response message = #{@message}." if @message
17
- message << " Documentation = #{@doc}." if @doc
18
- message << " Fields = #{@fields}." if @fields
19
- message
2
+ class ConnectionError < ShopifyAPI::Errors::HttpResponseError
3
+ attr_accessor :error_code, :fields
4
+
5
+ def initialize(response: nil)
6
+ unless response
7
+ empty_response = ShopifyAPI::Clients::HttpResponse.new(code: 200, headers: {}, body: "")
8
+ super(response: empty_response) and return
9
+ end
10
+
11
+ if response.is_a?(ShopifyAPI::Clients::HttpResponse)
12
+ super(response: response)
13
+ else
14
+ response = ShopifyAPI::Clients::HttpResponse.new(
15
+ code: 200,
16
+ headers: {},
17
+ body: response
18
+ )
19
+ super(response: response)
20
+ end
20
21
  end
21
22
  end
22
23
 
@@ -68,11 +69,11 @@ module ShopifyGraphql
68
69
  class TooManyRequests < ClientError # :nodoc:
69
70
  end
70
71
 
71
- # Graphql userErrors
72
- class UserError < ClientError # :nodoc:
73
- end
74
-
75
72
  # 5xx Server Error
76
73
  class ServerError < ConnectionError
77
74
  end
75
+
76
+ # Custom error for Graphql userErrors handling
77
+ class UserError < ConnectionError
78
+ end
78
79
  end
@@ -7,7 +7,8 @@ module ShopifyGraphql::Mutation
7
7
  end
8
8
  end
9
9
 
10
- delegate :execute, :handle_user_errors, to: :client
10
+ extend Forwardable
11
+ def_delegators :client, :execute, :handle_user_errors
11
12
 
12
13
  def client
13
14
  @client ||= ShopifyGraphql::Client.new
@@ -7,7 +7,8 @@ module ShopifyGraphql::Query
7
7
  end
8
8
  end
9
9
 
10
- delegate :execute, :handle_user_errors, to: :client
10
+ extend Forwardable
11
+ def_delegators :client, :execute, :handle_user_errors
11
12
 
12
13
  def client
13
14
  @client ||= ShopifyGraphql::Client.new
@@ -3,7 +3,8 @@ module ShopifyGraphql
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  class_methods do
6
- delegate :execute, :handle_user_errors, to: :client
6
+ extend Forwardable
7
+ def_delegators :client, :execute, :handle_user_errors
7
8
 
8
9
  def client
9
10
  ShopifyGraphql::Client.new
@@ -1,3 +1,3 @@
1
1
  module ShopifyGraphql
2
- VERSION = "1.2.6"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -1,8 +1,11 @@
1
1
  require 'shopify_api'
2
+ require 'active_support'
2
3
 
3
4
  require 'shopify_graphql/client'
4
5
  require 'shopify_graphql/configuration'
5
- require 'shopify_graphql/engine'
6
+ if defined?(Rails)
7
+ require 'shopify_graphql/engine'
8
+ end
6
9
  require 'shopify_graphql/exceptions'
7
10
  require 'shopify_graphql/mutation'
8
11
  require 'shopify_graphql/query'
@@ -15,9 +18,11 @@ require 'shopify_graphql/controller_concerns/payload_verification'
15
18
  require 'shopify_graphql/controller_concerns/webhook_verification'
16
19
 
17
20
  # jobs
18
- require 'shopify_graphql/jobs/create_webhooks_job'
19
- require 'shopify_graphql/jobs/destroy_webhooks_job'
20
- require 'shopify_graphql/jobs/update_webhooks_job'
21
+ if defined?(Rails)
22
+ require 'shopify_graphql/jobs/create_webhooks_job'
23
+ require 'shopify_graphql/jobs/destroy_webhooks_job'
24
+ require 'shopify_graphql/jobs/update_webhooks_job'
25
+ end
21
26
 
22
27
  # managers
23
28
  require 'shopify_graphql/managers/webhooks_manager'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.6
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kirill Platonov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-11 00:00:00.000000000 Z
11
+ date: 2024-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,28 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0
19
+ version: 6.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 6.0.0
26
+ version: 6.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 6.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 6.1.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: shopify_api
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '10.0'
47
+ version: '13.4'
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '10.0'
54
+ version: '13.4'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: shopify_app
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -64,10 +78,15 @@ files:
64
78
  - app/controllers/shopify_graphql/graphql_webhooks_controller.rb
65
79
  - app/graphql/shopify_graphql/app_subscription_fields.rb
66
80
  - app/graphql/shopify_graphql/cancel_subscription.rb
81
+ - app/graphql/shopify_graphql/create_bulk_mutation.rb
82
+ - app/graphql/shopify_graphql/create_bulk_query.rb
67
83
  - app/graphql/shopify_graphql/create_recurring_subscription.rb
84
+ - app/graphql/shopify_graphql/create_staged_uploads.rb
68
85
  - app/graphql/shopify_graphql/create_usage_subscription.rb
86
+ - app/graphql/shopify_graphql/current_shop.rb
69
87
  - app/graphql/shopify_graphql/delete_private_metafield.rb
70
88
  - app/graphql/shopify_graphql/get_app_subscription.rb
89
+ - app/graphql/shopify_graphql/get_bulk_operation.rb
71
90
  - app/graphql/shopify_graphql/upsert_private_metafield.rb
72
91
  - config/routes.rb
73
92
  - lib/shopify_graphql.rb
@@ -102,14 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
121
  requirements:
103
122
  - - ">="
104
123
  - !ruby/object:Gem::Version
105
- version: 2.7.0
124
+ version: '3.0'
106
125
  required_rubygems_version: !ruby/object:Gem::Requirement
107
126
  requirements:
108
127
  - - ">="
109
128
  - !ruby/object:Gem::Version
110
129
  version: '0'
111
130
  requirements: []
112
- rubygems_version: 3.4.3
131
+ rubygems_version: 3.5.11
113
132
  signing_key:
114
133
  specification_version: 4
115
134
  summary: Less painful way to work with Shopify Graphql API in Ruby.