shopify_api 13.2.0 → 13.3.1

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: b83eabed3d0c78f619d6061eb4bb92c389e1d15afca55225c243c0200a73f1f9
4
- data.tar.gz: 2d6858dda679bb4a65f2eddbbc3d746f6177eda1808109a1ebcce943ae5cb9b9
3
+ metadata.gz: b06b6f5bbea4f3ec2bfc6362e689d9323dff226bce6e89df9ad3678b07f5ccd1
4
+ data.tar.gz: 4aa27ed36ac0734941ad377a814ea13c9015ab44f7644bcfae61b69dabd749b3
5
5
  SHA512:
6
- metadata.gz: fb175ef43080cec7459eff1a7a40c3d0c48aabc8b69bd8fe10fba1bdcfb3c3ca9a9d4e5e5bd6184421a0639f06b18097efa151c912439198f28838a6f60b5b67
7
- data.tar.gz: 121a72ef9cc5b608b96d5be67a9404f739598708e9649fd720b21fa7614f2d53f162d2e12872441bf4419318d5cb7f13721606239ce21f9bbfa3868e1c4153f8
6
+ metadata.gz: aa6dde76b4b48dfefa13708a232354dfa8877befee6d9c83f6dc7e55dc9c7f9c3da0109e3a91b3aeb9b5bca954bc19d46b01e8f77c0c630e7eb140edf15b1c9e
7
+ data.tar.gz: 10527f74a2c026c49875e6c0a7caafd6c53e1137b8ee655a00b2524067e83439abebc47ac1f640cd6d74cd195c08dd8884243cebefdb270f1bced10e92ee100d
data/.github/CODEOWNERS CHANGED
@@ -1 +1 @@
1
- * @shopify/learn-libs-superteam
1
+ * @Shopify/client-libraries-app-templates
@@ -20,6 +20,10 @@ jobs:
20
20
  uses: ruby/setup-ruby@v1
21
21
  with:
22
22
  ruby-version: ${{ matrix.version }}
23
+ - name: Install OpenSSL
24
+ run: |
25
+ sudo apt-get update
26
+ sudo apt-get install -y libssl-dev
23
27
  - name: Run Bundle Commands
24
28
  run: |
25
29
  bundle config set --with docs
data/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 13.3.1
8
+
9
+ - [#1244](https://github.com/Shopify/shopify-api-ruby/pull/1244) Add `expired?` to `ShopifyAPI::Auth::Session` to check if the session is expired (mainly for user sessions)
10
+ - [#1249](https://github.com/Shopify/shopify-api-ruby/pull/1249) Fix bug where mandatory webhooks could not be processed
11
+ - [#1250](https://github.com/Shopify/shopify-api-ruby/pull/1250) Remove rails methods .empty? .present? that were breaking CI
12
+
13
+ ## 13.3.0
14
+
15
+ - [#1241](https://github.com/Shopify/shopify-api-ruby/pull/1241) Add `api_host` to `ShopifyAPI::Context.setup`, allowing the API host to be overridden in `ShopifyAPI::Clients::HttpClient`. This context option is intended for internal Shopify use only.
16
+ - [#1237](https://github.com/Shopify/shopify-api-ruby/pull/1237) Skip mandatory webhook topic registration/unregistrations
17
+ - [#1239](https://github.com/Shopify/shopify-api-ruby/pull/1239) Update `OAuth.validate_auth_callback` to use `ShopifyApi::Clients::HttpClient`.
18
+
7
19
  ## 13.2.0
8
20
 
9
21
  - [#1183](https://github.com/Shopify/shopify-api-ruby/pull/1189) Added string array support for fields parameter in Webhook::Registry
data/CONTRIBUTING.md CHANGED
@@ -1,9 +1,34 @@
1
1
 
2
2
  Submitting Issues
3
3
  -----------------
4
+ Submitting Issues
4
5
 
5
6
  Please open an issue here if you encounter a specific bug with this API client library or if something is documented here https://shopify.dev/docs/apps but is missing from this package.
6
7
 
7
8
  General questions about the Shopify API and usage of this package (not necessarily a bug) should be posted on the [Shopify forums](https://community.shopify.com/c/partners-and-developers/ct-p/appdev).
8
9
 
9
10
  When in doubt, post on the forum first. You'll likely have your questions answered more quickly if you post there; more people monitor the forum than Github.
11
+
12
+ In order for us to best triage the issue, please include steps to reproduce the issue as well as the impacted feature.
13
+
14
+ ## Roadmap
15
+
16
+ The focus of development efforts by maintainers of this project a roadmap will be proposed via PR and accessible at any point in the ROADMAP.md file.
17
+
18
+ Working with a pull request modify the [ROADMAP.md](https://github.com/Shopify/shopify-api-ruby/blob/aa0b7f9a5a9095ca11f3f93f9aecc72e8daa6bce/ROADMAP.md) allows us to invite community feedback on the direction while not adding another communication avenue. While there are certainly better tools for the job than a markdown file for this, we are aiming to keep a minimal toolset to help us better manage the communication channels that we have open.
19
+
20
+ If there are concerns with the direction and priorities of the maintainers, this roadmap PR is the appropriate place to share your concerns.
21
+
22
+ ## Submitting Pull Requests
23
+
24
+ We welcome pull requests and help from the community! PRs fixing bugs will take priority to triaging proposed net new functionality. If you do want to add a feature, we recommend opening an issue first exploring the appetite of the community / maintainers to ensure there is alignment on direction before you spend time on the PR.
25
+
26
+ ## Gem Architecture
27
+ Understanding how all the components of the Shopify App development stack work together will help best understand what level of abstraction a feature is meant to be applied. Please consider this architecture before introducing new functionally to ensure it is in the right place:
28
+
29
+ | Gem Name | Job |
30
+ |---|---|
31
+ | Shopify API (this gem) | Obtain a session, clients for APIs (REST, GraphQL), error handling, webhook management |
32
+ | REST Resources | Interfaces to the APIs. Response casting into defined objects with attributes/methods |
33
+ | Shopify App | Build Shopify app using Rails conventions. Oauth, webhook processing, persistence, etc |
34
+ | App Template | Template demonstrating how to use all these components in one starting boilerplate application |
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify_api (13.2.0)
4
+ shopify_api (13.3.1)
5
5
  activesupport
6
6
  concurrent-ruby
7
7
  hash_diff
@@ -16,20 +16,30 @@ PATH
16
16
  GEM
17
17
  remote: https://rubygems.org/
18
18
  specs:
19
- activesupport (7.0.7.2)
19
+ activesupport (7.1.2)
20
+ base64
21
+ bigdecimal
20
22
  concurrent-ruby (~> 1.0, >= 1.0.2)
23
+ connection_pool (>= 2.2.5)
24
+ drb
21
25
  i18n (>= 1.6, < 2)
22
26
  minitest (>= 5.1)
27
+ mutex_m
23
28
  tzinfo (~> 2.0)
24
29
  addressable (2.8.0)
25
30
  public_suffix (>= 2.0.2, < 5.0)
26
31
  ast (2.4.2)
32
+ base64 (0.2.0)
33
+ bigdecimal (3.1.4)
27
34
  byebug (11.1.3)
28
35
  coderay (1.1.3)
29
36
  concurrent-ruby (1.2.2)
37
+ connection_pool (2.4.1)
30
38
  crack (0.4.5)
31
39
  rexml
32
40
  diff-lcs (1.5.0)
41
+ drb (2.2.0)
42
+ ruby2_keywords
33
43
  fakefs (1.4.1)
34
44
  hash_diff (1.1.1)
35
45
  hashdiff (1.0.1)
@@ -40,19 +50,19 @@ GEM
40
50
  concurrent-ruby (~> 1.0)
41
51
  json (2.6.2)
42
52
  jwt (2.7.1)
43
- language_server-protocol (3.17.0.1)
44
53
  method_source (1.0.0)
45
54
  mini_mime (1.1.5)
46
55
  minitest (5.15.0)
47
56
  mocha (1.13.0)
48
57
  multi_xml (0.6.0)
58
+ mutex_m (0.2.0)
49
59
  netrc (0.11.0)
50
- oj (3.16.0)
51
- openssl (3.1.0)
60
+ oj (3.16.1)
61
+ openssl (3.2.0)
52
62
  parallel (1.22.1)
53
- parser (3.1.2.1)
63
+ parser (3.2.2.4)
54
64
  ast (~> 2.4.1)
55
- prettier_print (0.1.0)
65
+ racc
56
66
  pry (0.14.1)
57
67
  coderay (~> 1.1)
58
68
  method_source (~> 1.0)
@@ -60,6 +70,7 @@ GEM
60
70
  byebug (~> 11.0)
61
71
  pry (>= 0.13, < 0.15)
62
72
  public_suffix (4.0.6)
73
+ racc (1.7.1)
63
74
  rainbow (3.1.1)
64
75
  rake (13.0.6)
65
76
  rbi (0.0.15)
@@ -85,12 +96,9 @@ GEM
85
96
  rubocop (~> 1.35)
86
97
  rubocop-sorbet (0.6.11)
87
98
  rubocop (>= 0.90.0)
88
- ruby-lsp (0.3.2)
89
- language_server-protocol (~> 3.17.0)
90
- sorbet-runtime
91
- syntax_tree (>= 3.4)
92
99
  ruby-progressbar (1.11.0)
93
- securerandom (0.2.2)
100
+ ruby2_keywords (0.0.5)
101
+ securerandom (0.3.0)
94
102
  sorbet (0.5.10438)
95
103
  sorbet-static (= 0.5.10438)
96
104
  sorbet-runtime (0.5.10438)
@@ -104,8 +112,6 @@ GEM
104
112
  sorbet (>= 0.5.9204)
105
113
  sorbet-runtime (>= 0.5.9204)
106
114
  thor (>= 0.19.2)
107
- syntax_tree (3.6.1)
108
- prettier_print
109
115
  tapioca (0.10.2)
110
116
  bundler (>= 1.17.3)
111
117
  netrc (>= 0.11.0)
@@ -133,7 +139,7 @@ GEM
133
139
  yard-sorbet (0.7.0)
134
140
  sorbet-runtime (>= 0.5)
135
141
  yard (>= 0.9)
136
- zeitwerk (2.6.11)
142
+ zeitwerk (2.6.12)
137
143
 
138
144
  PLATFORMS
139
145
  arm64-darwin-21
@@ -149,7 +155,6 @@ DEPENDENCIES
149
155
  rubocop
150
156
  rubocop-shopify
151
157
  rubocop-sorbet
152
- ruby-lsp
153
158
  shopify_api!
154
159
  sorbet
155
160
  tapioca
data/ROADMAP.md ADDED
@@ -0,0 +1,10 @@
1
+ # Roadmap
2
+
3
+ |Priority|Description|Delivery Time frame|
4
+ |---|---|---|
5
+ |P0|Respond timely to open issues/Pull Requests|Ongoing|
6
+ |P1|Minor API release with support for 10-23 API version|Oct 6 - 13|
7
+ |P2|Restore dot notation access to GraphQL responses|Oct 26 - Dec 7|
8
+ |P2|Restrospection GQL queries to define types for GQL resources|October 26- Dec 7|
9
+ |P2|New token exchange authentication via optional feature flag|October 26- Dec 7|
10
+ |P3|[Extract REST resources into their own gem](https://github.com/Shopify/shopify-api-ruby/issues/1194)|Oct 26 - Dec 7|
@@ -75,8 +75,8 @@ module ShopifyAPI
75
75
  def decode_token(token, api_secret_key)
76
76
  JWT.decode(token, api_secret_key, true,
77
77
  { exp_leeway: JWT_EXPIRATION_LEEWAY, algorithm: "HS256" })[0]
78
- rescue
79
- raise ShopifyAPI::Errors::InvalidJwtTokenError, "Failed to parse session token '#{token}'"
78
+ rescue JWT::DecodeError => err
79
+ raise ShopifyAPI::Errors::InvalidJwtTokenError, "Error decoding session token: #{err.message}"
80
80
  end
81
81
  end
82
82
  end
@@ -70,15 +70,25 @@ module ShopifyAPI
70
70
  raise Errors::InvalidOauthError,
71
71
  "Invalid state in OAuth callback." unless state == auth_query.state
72
72
 
73
- # TODO: replace this call with the HTTP client once it is built
73
+ null_session = Auth::Session.new(shop: auth_query.shop)
74
74
  body = { client_id: Context.api_key, client_secret: Context.api_secret_key, code: auth_query.code }
75
- response = HTTParty.post("https://#{auth_query.shop}/admin/oauth/access_token", body: body)
76
- unless response.ok?
75
+
76
+ client = Clients::HttpClient.new(session: null_session, base_path: "/admin/oauth")
77
+ response = begin
78
+ client.request(
79
+ Clients::HttpRequest.new(
80
+ http_method: :post,
81
+ path: "access_token",
82
+ body: body,
83
+ body_type: "application/json",
84
+ ),
85
+ )
86
+ rescue ShopifyAPI::Errors::HttpResponseError => e
77
87
  raise Errors::RequestAccessTokenError,
78
- "Cannot complete OAuth process. Received a #{response.code} error while requesting access token."
88
+ "Cannot complete OAuth process. Received a #{e.code} error while requesting access token."
79
89
  end
80
- session_params = response.to_h
81
90
 
91
+ session_params = T.cast(response.body, T::Hash[String, T.untyped]).to_h
82
92
  session = create_new_session(session_params, auth_query.shop)
83
93
 
84
94
  cookie = if Context.embedded?
@@ -35,6 +35,11 @@ module ShopifyAPI
35
35
  @is_online
36
36
  end
37
37
 
38
+ sig { returns(T::Boolean) }
39
+ def expired?
40
+ @expires ? @expires < Time.now : false
41
+ end
42
+
38
43
  sig do
39
44
  params(
40
45
  shop: String,
@@ -13,7 +13,9 @@ module ShopifyAPI
13
13
  session ||= Context.active_session
14
14
  raise Errors::NoActiveSessionError, "No passed or active session" unless session
15
15
 
16
- @base_uri = T.let("https://#{session.shop}", String)
16
+ api_host = Context.api_host
17
+
18
+ @base_uri = T.let("https://#{api_host || session.shop}", String)
17
19
  @base_uri_and_path = T.let("#{@base_uri}#{base_path}", String)
18
20
 
19
21
  user_agent_prefix = Context.user_agent_prefix.nil? ? "" : "#{Context.user_agent_prefix} | "
@@ -23,6 +25,8 @@ module ShopifyAPI
23
25
  "Accept": "application/json",
24
26
  }, T::Hash[T.any(Symbol, String), T.untyped])
25
27
 
28
+ @headers["Host"] = session.shop unless api_host.nil?
29
+
26
30
  unless session.access_token.nil? || T.must(session.access_token).empty?
27
31
  @headers["X-Shopify-Access-Token"] = T.cast(session.access_token, String)
28
32
  end
@@ -8,6 +8,7 @@ module ShopifyAPI
8
8
  @api_key = T.let("", String)
9
9
  @api_secret_key = T.let("", String)
10
10
  @api_version = T.let(LATEST_SUPPORTED_ADMIN_VERSION, String)
11
+ @api_host = T.let(nil, T.nilable(String))
11
12
  @scope = T.let(Auth::AuthScopes.new, Auth::AuthScopes)
12
13
  @is_private = T.let(false, T::Boolean)
13
14
  @private_shop = T.let(nil, T.nilable(String))
@@ -41,6 +42,7 @@ module ShopifyAPI
41
42
  private_shop: T.nilable(String),
42
43
  user_agent_prefix: T.nilable(String),
43
44
  old_api_secret_key: T.nilable(String),
45
+ api_host: T.nilable(String),
44
46
  ).void
45
47
  end
46
48
  def setup(
@@ -56,7 +58,8 @@ module ShopifyAPI
56
58
  host: ENV["HOST"] || "https://#{host_name}",
57
59
  private_shop: nil,
58
60
  user_agent_prefix: nil,
59
- old_api_secret_key: nil
61
+ old_api_secret_key: nil,
62
+ api_host: nil
60
63
  )
61
64
  unless ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS.include?(api_version)
62
65
  raise Errors::UnsupportedVersionError,
@@ -66,6 +69,7 @@ module ShopifyAPI
66
69
  @api_key = api_key
67
70
  @api_secret_key = api_secret_key
68
71
  @api_version = api_version
72
+ @api_host = api_host
69
73
  @host = T.let(host, T.nilable(String))
70
74
  @is_private = is_private
71
75
  @scope = Auth::AuthScopes.new(scope)
@@ -130,7 +134,7 @@ module ShopifyAPI
130
134
  end
131
135
 
132
136
  sig { returns(T.nilable(String)) }
133
- attr_reader :private_shop, :user_agent_prefix, :old_api_secret_key, :host
137
+ attr_reader :private_shop, :user_agent_prefix, :old_api_secret_key, :host, :api_host
134
138
 
135
139
  sig { returns(T::Boolean) }
136
140
  def embedded?
@@ -14,7 +14,7 @@ module ShopifyAPI
14
14
  return false unless verifiable_query.hmac
15
15
 
16
16
  result = validate_signature(verifiable_query, Context.api_secret_key)
17
- if result || Context.old_api_secret_key.blank?
17
+ if result || Context.old_api_secret_key.nil? || T.must(Context.old_api_secret_key).empty?
18
18
  result
19
19
  else
20
20
  validate_signature(verifiable_query, T.must(Context.old_api_secret_key))
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ShopifyAPI
5
- VERSION = "13.2.0"
5
+ VERSION = "13.3.1"
6
6
  end
@@ -5,6 +5,11 @@ module ShopifyAPI
5
5
  module Webhooks
6
6
  class Registry
7
7
  @registry = T.let({}, T::Hash[String, Registration])
8
+ MANDATORY_TOPICS = T.let([
9
+ "shop/redact",
10
+ "customers/redact",
11
+ "customers/data_request",
12
+ ].freeze, T::Array[String])
8
13
 
9
14
  class << self
10
15
  extend T::Sig
@@ -49,6 +54,8 @@ module ShopifyAPI
49
54
  ).returns(RegisterResult)
50
55
  end
51
56
  def register(topic:, session:)
57
+ return mandatory_registration_result(topic) if mandatory_webhook_topic?(topic)
58
+
52
59
  registration = @registry[topic]
53
60
 
54
61
  unless registration
@@ -76,6 +83,17 @@ module ShopifyAPI
76
83
  RegisterResult.new(topic: topic, success: registered, body: register_body)
77
84
  end
78
85
 
86
+ sig do
87
+ params(topic: String).returns(RegisterResult)
88
+ end
89
+ def mandatory_registration_result(topic)
90
+ RegisterResult.new(
91
+ topic: topic,
92
+ success: false,
93
+ body: "Mandatory webhooks are to be registered in the partners dashboard",
94
+ )
95
+ end
96
+
79
97
  sig do
80
98
  params(
81
99
  session: Auth::Session,
@@ -101,6 +119,8 @@ module ShopifyAPI
101
119
  ).returns(T::Hash[String, T.untyped])
102
120
  end
103
121
  def unregister(topic:, session:)
122
+ return { "response": nil } if mandatory_webhook_topic?(topic)
123
+
104
124
  client = Clients::Graphql::Admin.new(session: session)
105
125
 
106
126
  webhook_id = get_webhook_id(topic: topic, client: client)
@@ -213,6 +233,13 @@ module ShopifyAPI
213
233
  def registration_sucessful?(body, mutation_name)
214
234
  !body.dig("data", mutation_name, "webhookSubscription").nil?
215
235
  end
236
+
237
+ # Mandatory webhooks are subscribed to via the partner dashboard not the API
238
+ # https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks
239
+ sig { params(topic: String).returns(T::Boolean) }
240
+ def mandatory_webhook_topic?(topic)
241
+ MANDATORY_TOPICS.include?(topic)
242
+ end
216
243
  end
217
244
  end
218
245
  end
data/shopify_api.gemspec CHANGED
@@ -48,7 +48,6 @@ Gem::Specification.new do |s|
48
48
  s.add_development_dependency("rubocop")
49
49
  s.add_development_dependency("rubocop-shopify")
50
50
  s.add_development_dependency("rubocop-sorbet")
51
- s.add_development_dependency("ruby-lsp")
52
51
  s.add_development_dependency("sorbet")
53
52
  s.add_development_dependency("tapioca")
54
53
  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: 13.2.0
4
+ version: 13.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-10 00:00:00.000000000 Z
11
+ date: 2023-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -220,20 +220,6 @@ dependencies:
220
220
  - - ">="
221
221
  - !ruby/object:Gem::Version
222
222
  version: '0'
223
- - !ruby/object:Gem::Dependency
224
- name: ruby-lsp
225
- requirement: !ruby/object:Gem::Requirement
226
- requirements:
227
- - - ">="
228
- - !ruby/object:Gem::Version
229
- version: '0'
230
- type: :development
231
- prerelease: false
232
- version_requirements: !ruby/object:Gem::Requirement
233
- requirements:
234
- - - ">="
235
- - !ruby/object:Gem::Version
236
- version: '0'
237
223
  - !ruby/object:Gem::Dependency
238
224
  name: sorbet
239
225
  requirement: !ruby/object:Gem::Requirement
@@ -296,6 +282,7 @@ files:
296
282
  - LICENSE
297
283
  - README.md
298
284
  - RELEASING.md
285
+ - ROADMAP.md
299
286
  - Rakefile
300
287
  - SECURITY.md
301
288
  - bin/tapioca
@@ -970,7 +957,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
970
957
  - !ruby/object:Gem::Version
971
958
  version: '0'
972
959
  requirements: []
973
- rubygems_version: 3.4.20
960
+ rubygems_version: 3.4.21
974
961
  signing_key:
975
962
  specification_version: 4
976
963
  summary: The gem for accessing the Shopify API