shopify_api 13.2.0 → 13.3.1

Sign up to get free protection for your applications and to get access to all the features.
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