shopify_api 14.8.0 → 14.9.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 +4 -4
- data/.github/workflows/close-waiting-for-response-issues.yml +2 -2
- data/.ruby-version +1 -1
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/docs/usage/graphql_storefront.md +1 -1
- data/docs/usage/oauth.md +41 -4
- data/lib/shopify_api/admin_versions.rb +5 -1
- data/lib/shopify_api/auth/client_credentials.rb +52 -0
- data/lib/shopify_api/auth/jwt_payload.rb +18 -15
- data/lib/shopify_api/auth/oauth.rb +15 -1
- data/lib/shopify_api/auth/session.rb +4 -1
- data/lib/shopify_api/clients/http_client.rb +26 -1
- data/lib/shopify_api/context.rb +3 -2
- data/lib/shopify_api/rest/resources/2025_04/abandoned_checkout.rb +194 -0
- data/lib/shopify_api/rest/resources/2025_04/access_scope.rb +62 -0
- data/lib/shopify_api/rest/resources/2025_04/apple_pay_certificate.rb +109 -0
- data/lib/shopify_api/rest/resources/2025_04/application_charge.rb +113 -0
- data/lib/shopify_api/rest/resources/2025_04/application_credit.rb +95 -0
- data/lib/shopify_api/rest/resources/2025_04/article.rb +269 -0
- data/lib/shopify_api/rest/resources/2025_04/asset.rb +122 -0
- data/lib/shopify_api/rest/resources/2025_04/assigned_fulfillment_order.rb +92 -0
- data/lib/shopify_api/rest/resources/2025_04/balance.rb +58 -0
- data/lib/shopify_api/rest/resources/2025_04/blog.rb +166 -0
- data/lib/shopify_api/rest/resources/2025_04/cancellation_request.rb +87 -0
- data/lib/shopify_api/rest/resources/2025_04/carrier_service.rb +120 -0
- data/lib/shopify_api/rest/resources/2025_04/checkout.rb +213 -0
- data/lib/shopify_api/rest/resources/2025_04/collect.rb +146 -0
- data/lib/shopify_api/rest/resources/2025_04/collection.rb +114 -0
- data/lib/shopify_api/rest/resources/2025_04/collection_listing.rb +159 -0
- data/lib/shopify_api/rest/resources/2025_04/comment.rb +287 -0
- data/lib/shopify_api/rest/resources/2025_04/country.rb +141 -0
- data/lib/shopify_api/rest/resources/2025_04/currency.rb +61 -0
- data/lib/shopify_api/rest/resources/2025_04/custom_collection.rb +191 -0
- data/lib/shopify_api/rest/resources/2025_04/customer.rb +328 -0
- data/lib/shopify_api/rest/resources/2025_04/customer_address.rb +215 -0
- data/lib/shopify_api/rest/resources/2025_04/deprecated_api_call.rb +61 -0
- data/lib/shopify_api/rest/resources/2025_04/discount_code.rb +226 -0
- data/lib/shopify_api/rest/resources/2025_04/dispute.rb +115 -0
- data/lib/shopify_api/rest/resources/2025_04/dispute_evidence.rb +121 -0
- data/lib/shopify_api/rest/resources/2025_04/dispute_file_upload.rb +85 -0
- data/lib/shopify_api/rest/resources/2025_04/draft_order.rb +279 -0
- data/lib/shopify_api/rest/resources/2025_04/event.rb +152 -0
- data/lib/shopify_api/rest/resources/2025_04/fulfillment.rb +235 -0
- data/lib/shopify_api/rest/resources/2025_04/fulfillment_event.rb +167 -0
- data/lib/shopify_api/rest/resources/2025_04/fulfillment_order.rb +326 -0
- data/lib/shopify_api/rest/resources/2025_04/fulfillment_request.rb +116 -0
- data/lib/shopify_api/rest/resources/2025_04/fulfillment_service.rb +134 -0
- data/lib/shopify_api/rest/resources/2025_04/gift_card.rb +222 -0
- data/lib/shopify_api/rest/resources/2025_04/gift_card_adjustment.rb +122 -0
- data/lib/shopify_api/rest/resources/2025_04/image.rb +161 -0
- data/lib/shopify_api/rest/resources/2025_04/inventory_item.rb +112 -0
- data/lib/shopify_api/rest/resources/2025_04/inventory_level.rb +183 -0
- data/lib/shopify_api/rest/resources/2025_04/location.rb +171 -0
- data/lib/shopify_api/rest/resources/2025_04/locations_for_move.rb +60 -0
- data/lib/shopify_api/rest/resources/2025_04/marketing_event.rb +213 -0
- data/lib/shopify_api/rest/resources/2025_04/metafield.rb +348 -0
- data/lib/shopify_api/rest/resources/2025_04/mobile_platform_application.rb +120 -0
- data/lib/shopify_api/rest/resources/2025_04/order.rb +503 -0
- data/lib/shopify_api/rest/resources/2025_04/order_risk.rb +148 -0
- data/lib/shopify_api/rest/resources/2025_04/page.rb +198 -0
- data/lib/shopify_api/rest/resources/2025_04/payment.rb +98 -0
- data/lib/shopify_api/rest/resources/2025_04/payment_gateway.rb +147 -0
- data/lib/shopify_api/rest/resources/2025_04/payment_transaction.rb +117 -0
- data/lib/shopify_api/rest/resources/2025_04/payout.rb +101 -0
- data/lib/shopify_api/rest/resources/2025_04/policy.rb +73 -0
- data/lib/shopify_api/rest/resources/2025_04/price_rule.rb +227 -0
- data/lib/shopify_api/rest/resources/2025_04/product.rb +227 -0
- data/lib/shopify_api/rest/resources/2025_04/product_listing.rb +200 -0
- data/lib/shopify_api/rest/resources/2025_04/product_resource_feedback.rb +92 -0
- data/lib/shopify_api/rest/resources/2025_04/province.rb +136 -0
- data/lib/shopify_api/rest/resources/2025_04/recurring_application_charge.rb +184 -0
- data/lib/shopify_api/rest/resources/2025_04/redirect.rb +143 -0
- data/lib/shopify_api/rest/resources/2025_04/refund.rb +158 -0
- data/lib/shopify_api/rest/resources/2025_04/resource_feedback.rb +77 -0
- data/lib/shopify_api/rest/resources/2025_04/script_tag.rb +159 -0
- data/lib/shopify_api/rest/resources/2025_04/shipping_zone.rb +87 -0
- data/lib/shopify_api/rest/resources/2025_04/shop.rb +231 -0
- data/lib/shopify_api/rest/resources/2025_04/smart_collection.rb +220 -0
- data/lib/shopify_api/rest/resources/2025_04/storefront_access_token.rb +91 -0
- data/lib/shopify_api/rest/resources/2025_04/tender_transaction.rb +97 -0
- data/lib/shopify_api/rest/resources/2025_04/theme.rb +127 -0
- data/lib/shopify_api/rest/resources/2025_04/transaction.rb +194 -0
- data/lib/shopify_api/rest/resources/2025_04/usage_charge.rb +106 -0
- data/lib/shopify_api/rest/resources/2025_04/user.rb +142 -0
- data/lib/shopify_api/rest/resources/2025_04/variant.rb +212 -0
- data/lib/shopify_api/rest/resources/2025_04/webhook.rb +173 -0
- data/lib/shopify_api/utils/session_utils.rb +1 -1
- data/lib/shopify_api/version.rb +1 -1
- metadata +77 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80e32598197d0d0becb6f4fb8175cd9f0b7ca5ad83763a5934a55ff7c8a3f769
|
4
|
+
data.tar.gz: 38f4b87f9432966ac19017991fb85f8696e0adcd6e047378b3b6c2c35e088f66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2be22dec439c86f297e71e19fa0fbc04f5bb31eb296fd0632567caee65874985b3595d4bf9e9452a09540cb76c302117413e397ba90342e590882b72fbcf7dc8
|
7
|
+
data.tar.gz: 2d22864ebcb109ba38cef3a461696d7c7b19426c4f8e75b9041b3fc48e26af957a30a1dfa02895d7f2da1e000d44bf797ad5b7529ebd5240a1d19a56a8fa60df
|
@@ -13,8 +13,8 @@ jobs:
|
|
13
13
|
actions: 'close-issues'
|
14
14
|
token: ${{ secrets.GITHUB_TOKEN }}
|
15
15
|
labels: 'Waiting for Response'
|
16
|
-
inactive-day:
|
16
|
+
inactive-day: 14
|
17
17
|
body: |
|
18
|
-
We are closing this issue because we did not hear back regarding additional details we needed to resolve this issue. If the issue persists and you are able to provide the missing clarification we need, feel free to
|
18
|
+
We are closing this issue because we did not hear back regarding additional details we needed to resolve this issue. If the issue persists and you are able to provide the missing clarification we need, feel free to create a new issue with the additional information.
|
19
19
|
|
20
20
|
We appreciate your understanding as we try to manage our number of open issues.
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.3.7
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,14 @@
|
|
3
3
|
Note: For changes to the API, see https://shopify.dev/changelog?filter=api
|
4
4
|
## Unreleased
|
5
5
|
|
6
|
+
## 14.9.0
|
7
|
+
|
8
|
+
- [#1362](https://github.com/Shopify/shopify-api-ruby/pull/1362) Add support for client credentials grant
|
9
|
+
- [#1372](https://github.com/Shopify/shopify-api-ruby/pull/1372) Add support for 2025-04 API version
|
10
|
+
- [#1369](https://github.com/Shopify/shopify-api-ruby/pull/1369) Make `sub` and `sid` jwt claims optional (Checkout ui extension support)
|
11
|
+
- [#1370](https://github.com/Shopify/shopify-api-ruby/pull/1370) Add support for Shopify internal hosts
|
12
|
+
- [#1366](https://github.com/Shopify/shopify-api-ruby/pull/1366) Add support for release candidate API versions
|
13
|
+
|
6
14
|
## 14.8.0
|
7
15
|
|
8
16
|
- [#1355](https://github.com/Shopify/shopify-api-ruby/pull/1355) Add support for 2025-01 API version
|
data/Gemfile.lock
CHANGED
@@ -39,7 +39,7 @@ QUERY
|
|
39
39
|
|
40
40
|
# You may not need the "Shopify-Storefront-Buyer-IP" header, see its documentation:
|
41
41
|
# https://shopify.dev/docs/api/usage/authentication#making-server-side-requests
|
42
|
-
response = client.query(query: query, headers: { "Shopify-Storefront-Buyer-IP": request.
|
42
|
+
response = client.query(query: query, headers: { "Shopify-Storefront-Buyer-IP": request.remote_ip })
|
43
43
|
# do something with the returned data
|
44
44
|
```
|
45
45
|
|
data/docs/usage/oauth.md
CHANGED
@@ -11,7 +11,8 @@ For more information on authenticating a Shopify app please see the [Types of Au
|
|
11
11
|
- [Note about Rails](#note-about-rails)
|
12
12
|
- [Performing OAuth](#performing-oauth-1)
|
13
13
|
- [Token Exchange](#token-exchange)
|
14
|
-
- [Authorization Code Grant
|
14
|
+
- [Authorization Code Grant](#authorization-code-grant)
|
15
|
+
- [Client Credentials Grant](#client-credentials-grant)
|
15
16
|
- [Using OAuth Session to make authenticated API calls](#using-oauth-session-to-make-authenticated-api-calls)
|
16
17
|
|
17
18
|
## Session Persistence
|
@@ -31,10 +32,14 @@ with [token exchange](#token-exchange) instead of the authorization code grant f
|
|
31
32
|
- Recommended and is only available for embedded apps
|
32
33
|
- Doesn't require redirects, which makes authorization faster and prevents flickering when loading the app
|
33
34
|
- Access scope changes are handled by [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
|
34
|
-
2. [Authorization Code Grant
|
35
|
+
2. [Authorization Code Grant](#authorization-code-grant)
|
35
36
|
- OAuth flow that requires the app to redirect the user to Shopify for installation/authorization of the app to access the shop's data.
|
36
37
|
- Suitable for non-embedded apps
|
37
38
|
- Installations, and access scope changes are managed by the app
|
39
|
+
3. [Client Credentials Grant](#client-credentials-grant)
|
40
|
+
- Suitable for apps without a UI
|
41
|
+
- Doesn't require user interaction in the browser
|
42
|
+
- Access scope changes are handled by [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
|
38
43
|
|
39
44
|
## Note about Rails
|
40
45
|
If using in the Rails framework, we highly recommend you use the [shopify_app](https://github.com/Shopify/shopify_app) gem to perform OAuth, you won't have to follow the instructions below to start your own OAuth flow.
|
@@ -94,7 +99,7 @@ end
|
|
94
99
|
|
95
100
|
```
|
96
101
|
|
97
|
-
### Authorization Code Grant
|
102
|
+
### Authorization Code Grant
|
98
103
|
##### Steps
|
99
104
|
1. [Add a route to start OAuth](#1-add-a-route-to-start-oauth)
|
100
105
|
2. [Add an Oauth callback route](#2-add-an-oauth-callback-route)
|
@@ -265,9 +270,41 @@ def callback
|
|
265
270
|
end
|
266
271
|
end
|
267
272
|
```
|
268
|
-
|
269
273
|
⚠️ You can see a concrete example in the `ShopifyApp` gem's [CallbackController](https://github.com/Shopify/shopify_app/blob/main/app/controllers/shopify_app/callback_controller.rb).
|
270
274
|
|
275
|
+
### Client Credentials Grant
|
276
|
+
|
277
|
+
> [!NOTE]
|
278
|
+
> You can only use the client credentials grant when building apps for your own organization.
|
279
|
+
|
280
|
+
> [!WARNING]
|
281
|
+
> [token exchange](#token-exchange) (for embedded apps) or the [authorization code grant](#authorization-code-grant) should be used instead of the client credentials grant, if your app is a browser based web app.
|
282
|
+
|
283
|
+
#### Perform Client Credentials Grant
|
284
|
+
Use [`ShopifyAPI::Auth::ClientCredentials`](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/auth/client_credentials.rb) to
|
285
|
+
exchange the [app's client ID and client secret](https://shopify.dev/docs/apps/build/authentication-authorization/client-secrets) for an access token.
|
286
|
+
#### Input
|
287
|
+
| Parameter | Type | Required? | Default Value | Notes |
|
288
|
+
| -------------- | ---------------------- | :-------: | :-----------: | ----------------------------------------------------------------------------------------------------------- |
|
289
|
+
| `shop` | `String` | Yes | - | A Shopify domain name in the form `{exampleshop}.myshopify.com`. |
|
290
|
+
|
291
|
+
#### Output
|
292
|
+
This method returns the new `ShopifyAPI::Auth::Session` object from the client credentials grant, your app should store this `Session` object to be used later [when making authenticated API calls](#using-oauth-session-to-make-authenticated-api-calls).
|
293
|
+
|
294
|
+
#### Example
|
295
|
+
```ruby
|
296
|
+
|
297
|
+
# `shop` is the shop domain name - "this-is-my-example-shop.myshopify.com"
|
298
|
+
|
299
|
+
def authenticate(shop)
|
300
|
+
session = ShopifyAPI::Auth::ClientCredentials.client_credentials(
|
301
|
+
shop: shop,
|
302
|
+
)
|
303
|
+
SessionRepository.store_session(session)
|
304
|
+
end
|
305
|
+
|
306
|
+
```
|
307
|
+
|
271
308
|
## Using OAuth Session to make authenticated API calls
|
272
309
|
Once your OAuth flow is complete, and you have persisted your `Session` object, you may use that `Session` object to make authenticated API calls.
|
273
310
|
|
@@ -5,6 +5,8 @@ module ShopifyAPI
|
|
5
5
|
module AdminVersions
|
6
6
|
SUPPORTED_ADMIN_VERSIONS = T.let([
|
7
7
|
"unstable",
|
8
|
+
"2025-07",
|
9
|
+
"2025-04",
|
8
10
|
"2025-01",
|
9
11
|
"2024-10",
|
10
12
|
"2024-07",
|
@@ -20,9 +22,11 @@ module ShopifyAPI
|
|
20
22
|
"2022-01",
|
21
23
|
], T::Array[String])
|
22
24
|
|
23
|
-
LATEST_SUPPORTED_ADMIN_VERSION = T.let("2025-
|
25
|
+
LATEST_SUPPORTED_ADMIN_VERSION = T.let("2025-04", String)
|
26
|
+
RELEASE_CANDIDATE_ADMIN_VERSION = T.let("2025-07", String)
|
24
27
|
end
|
25
28
|
|
26
29
|
SUPPORTED_ADMIN_VERSIONS = ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS
|
27
30
|
LATEST_SUPPORTED_ADMIN_VERSION = ShopifyAPI::AdminVersions::LATEST_SUPPORTED_ADMIN_VERSION
|
31
|
+
RELEASE_CANDIDATE_ADMIN_VERSION = ShopifyAPI::AdminVersions::RELEASE_CANDIDATE_ADMIN_VERSION
|
28
32
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ShopifyAPI
|
5
|
+
module Auth
|
6
|
+
module ClientCredentials
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
CLIENT_CREDENTIALS_GRANT_TYPE = "client_credentials"
|
10
|
+
|
11
|
+
class << self
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig do
|
15
|
+
params(
|
16
|
+
shop: String,
|
17
|
+
).returns(ShopifyAPI::Auth::Session)
|
18
|
+
end
|
19
|
+
def client_credentials(shop:)
|
20
|
+
unless ShopifyAPI::Context.setup?
|
21
|
+
raise ShopifyAPI::Errors::ContextNotSetupError,
|
22
|
+
"ShopifyAPI::Context not setup, please call ShopifyAPI::Context.setup"
|
23
|
+
end
|
24
|
+
|
25
|
+
shop_session = ShopifyAPI::Auth::Session.new(shop: shop)
|
26
|
+
body = {
|
27
|
+
client_id: ShopifyAPI::Context.api_key,
|
28
|
+
client_secret: ShopifyAPI::Context.api_secret_key,
|
29
|
+
grant_type: CLIENT_CREDENTIALS_GRANT_TYPE,
|
30
|
+
}
|
31
|
+
|
32
|
+
client = Clients::HttpClient.new(session: shop_session, base_path: "/admin/oauth")
|
33
|
+
response =
|
34
|
+
client.request(
|
35
|
+
Clients::HttpRequest.new(
|
36
|
+
http_method: :post,
|
37
|
+
path: "access_token",
|
38
|
+
body: body,
|
39
|
+
body_type: "application/json",
|
40
|
+
),
|
41
|
+
)
|
42
|
+
response_hash = T.cast(response.body, T::Hash[String, T.untyped]).to_h
|
43
|
+
|
44
|
+
Session.from(
|
45
|
+
shop: shop,
|
46
|
+
access_token_response: Oauth::AccessTokenResponse.from_hash(response_hash),
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -10,11 +10,14 @@ module ShopifyAPI
|
|
10
10
|
JWT_EXPIRATION_LEEWAY = JWT_LEEWAY
|
11
11
|
|
12
12
|
sig { returns(String) }
|
13
|
-
attr_reader :iss, :dest, :aud, :
|
13
|
+
attr_reader :iss, :dest, :aud, :jti
|
14
14
|
|
15
15
|
sig { returns(Integer) }
|
16
16
|
attr_reader :exp, :nbf, :iat
|
17
17
|
|
18
|
+
sig { returns(T.nilable(String)) }
|
19
|
+
attr_reader :sub, :sid
|
20
|
+
|
18
21
|
alias_method :expire_at, :exp
|
19
22
|
|
20
23
|
sig { params(token: String).void }
|
@@ -30,12 +33,12 @@ module ShopifyAPI
|
|
30
33
|
@iss = T.let(payload_hash["iss"], String)
|
31
34
|
@dest = T.let(payload_hash["dest"], String)
|
32
35
|
@aud = T.let(payload_hash["aud"], String)
|
33
|
-
@sub = T.let(payload_hash["sub"], String)
|
36
|
+
@sub = T.let(payload_hash["sub"], T.nilable(String))
|
34
37
|
@exp = T.let(payload_hash["exp"], Integer)
|
35
38
|
@nbf = T.let(payload_hash["nbf"], Integer)
|
36
39
|
@iat = T.let(payload_hash["iat"], Integer)
|
37
40
|
@jti = T.let(payload_hash["jti"], String)
|
38
|
-
@sid = T.let(payload_hash["sid"], String)
|
41
|
+
@sid = T.let(payload_hash["sid"], T.nilable(String))
|
39
42
|
|
40
43
|
raise ShopifyAPI::Errors::InvalidJwtTokenError,
|
41
44
|
"Session token had invalid API key" unless @aud == Context.api_key
|
@@ -47,19 +50,9 @@ module ShopifyAPI
|
|
47
50
|
end
|
48
51
|
alias_method :shopify_domain, :shop
|
49
52
|
|
50
|
-
sig { returns(Integer) }
|
53
|
+
sig { returns(T.nilable(Integer)) }
|
51
54
|
def shopify_user_id
|
52
|
-
@sub.to_i
|
53
|
-
end
|
54
|
-
|
55
|
-
# TODO: Remove before releasing v11
|
56
|
-
sig { params(shop: String).returns(T::Boolean) }
|
57
|
-
def validate_shop(shop)
|
58
|
-
Context.logger.warn(
|
59
|
-
"Deprecation notice: ShopifyAPI::Auth::JwtPayload.validate_shop no longer checks the given shop and always " \
|
60
|
-
"returns true. It will be removed in v11.",
|
61
|
-
)
|
62
|
-
true
|
55
|
+
@sub.to_i if user_id_sub? && admin_session_token?
|
63
56
|
end
|
64
57
|
|
65
58
|
alias_method :eql?, :==
|
@@ -86,6 +79,16 @@ module ShopifyAPI
|
|
86
79
|
rescue JWT::DecodeError => err
|
87
80
|
raise ShopifyAPI::Errors::InvalidJwtTokenError, "Error decoding session token: #{err.message}"
|
88
81
|
end
|
82
|
+
|
83
|
+
sig { returns(T::Boolean) }
|
84
|
+
def admin_session_token?
|
85
|
+
@iss.end_with?("/admin")
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { returns(T::Boolean) }
|
89
|
+
def user_id_sub?
|
90
|
+
@sub&.match?(/\A\d+\z/) || false
|
91
|
+
end
|
89
92
|
end
|
90
93
|
end
|
91
94
|
end
|
@@ -46,8 +46,8 @@ module ShopifyAPI
|
|
46
46
|
}
|
47
47
|
|
48
48
|
query_string = URI.encode_www_form(query)
|
49
|
+
auth_route = auth_base_uri(shop) + "/oauth/authorize?#{query_string}"
|
49
50
|
|
50
|
-
auth_route = "https://#{shop}/admin/oauth/authorize?#{query_string}"
|
51
51
|
{ auth_route: auth_route, cookie: cookie }
|
52
52
|
end
|
53
53
|
|
@@ -106,6 +106,20 @@ module ShopifyAPI
|
|
106
106
|
|
107
107
|
{ session: session, cookie: cookie }
|
108
108
|
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
sig { params(shop: String).returns(String) }
|
113
|
+
def auth_base_uri(shop)
|
114
|
+
return "https://#{shop}/admin" unless defined?(DevServer)
|
115
|
+
|
116
|
+
# For first-party apps in development only, we leverage DevServer to build the admin base URI
|
117
|
+
admin_web = T.unsafe(Object.const_get("DevServer")).new("web") # rubocop:disable Sorbet/ConstantsFromStrings
|
118
|
+
admin_host = admin_web.host!(nonstandard_host_prefix: "admin")
|
119
|
+
shop_name = shop.split(".").first
|
120
|
+
|
121
|
+
"https://#{admin_host}/store/#{shop_name}"
|
122
|
+
end
|
109
123
|
end
|
110
124
|
end
|
111
125
|
end
|
@@ -95,13 +95,16 @@ module ShopifyAPI
|
|
95
95
|
|
96
96
|
if is_online
|
97
97
|
associated_user = T.must(access_token_response.associated_user)
|
98
|
-
expires = Time.now + access_token_response.expires_in.to_i
|
99
98
|
associated_user_scope = access_token_response.associated_user_scope
|
100
99
|
id = "#{shop}_#{associated_user.id}"
|
101
100
|
else
|
102
101
|
id = "offline_#{shop}"
|
103
102
|
end
|
104
103
|
|
104
|
+
if access_token_response.expires_in
|
105
|
+
expires = Time.now + access_token_response.expires_in.to_i
|
106
|
+
end
|
107
|
+
|
105
108
|
new(
|
106
109
|
id: id,
|
107
110
|
shop: shop,
|
@@ -40,13 +40,17 @@ module ShopifyAPI
|
|
40
40
|
headers["Content-Type"] = T.must(request.body_type) if request.body_type
|
41
41
|
headers = headers.merge(T.must(request.extra_headers)) if request.extra_headers
|
42
42
|
|
43
|
+
parsed_uri = URI(request_url(request))
|
44
|
+
|
45
|
+
headers = append_first_party_development_headers(headers, parsed_uri)
|
46
|
+
|
43
47
|
tries = 0
|
44
48
|
response = HttpResponse.new(code: 0, headers: {}, body: "")
|
45
49
|
while tries < request.tries
|
46
50
|
tries += 1
|
47
51
|
res = T.cast(HTTParty.send(
|
48
52
|
request.http_method,
|
49
|
-
|
53
|
+
parsed_uri.to_s,
|
50
54
|
headers: headers,
|
51
55
|
query: request.query,
|
52
56
|
body: request.body.class == Hash ? T.unsafe(request.body).to_json : request.body,
|
@@ -115,6 +119,27 @@ module ShopifyAPI
|
|
115
119
|
end
|
116
120
|
body.to_json
|
117
121
|
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
sig do
|
126
|
+
params(
|
127
|
+
headers: T::Hash[T.any(Symbol, String), T.untyped],
|
128
|
+
parsed_uri: URI::Generic,
|
129
|
+
).returns(T::Hash[T.any(Symbol, String), T.untyped])
|
130
|
+
end
|
131
|
+
def append_first_party_development_headers(headers, parsed_uri)
|
132
|
+
return headers unless defined?(DevServer)
|
133
|
+
return headers unless headers["Host"]&.include?(".my.shop.dev") || parsed_uri.host&.include?(".my.shop.dev")
|
134
|
+
|
135
|
+
# These headers are only used for first party applications in development mode
|
136
|
+
headers["x-forwarded-host"] = headers["Host"] || parsed_uri.host
|
137
|
+
headers["Host"] = T.unsafe(
|
138
|
+
Object.const_get("DevServer::Core"), # rubocop:disable Sorbet/ConstantsFromStrings
|
139
|
+
).new.host!(:app)
|
140
|
+
|
141
|
+
headers
|
142
|
+
end
|
118
143
|
end
|
119
144
|
end
|
120
145
|
end
|
data/lib/shopify_api/context.rb
CHANGED
@@ -101,10 +101,11 @@ module ShopifyAPI
|
|
101
101
|
@rest_resource_loader&.setup
|
102
102
|
@rest_resource_loader&.unload
|
103
103
|
|
104
|
-
# No resources for the unstable version
|
105
|
-
return if api_version == "unstable"
|
104
|
+
# No resources for the unstable version or the release candidate version
|
105
|
+
return if api_version == "unstable" || api_version == RELEASE_CANDIDATE_ADMIN_VERSION
|
106
106
|
|
107
107
|
version_folder_name = api_version.gsub("-", "_")
|
108
|
+
|
108
109
|
path = "#{__dir__}/rest/resources/#{version_folder_name}"
|
109
110
|
|
110
111
|
unless Dir.exist?(path)
|
@@ -0,0 +1,194 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
########################################################################################################################
|
5
|
+
# This file is auto-generated. If you have an issue, please create a GitHub issue. #
|
6
|
+
########################################################################################################################
|
7
|
+
|
8
|
+
module ShopifyAPI
|
9
|
+
class AbandonedCheckout < ShopifyAPI::Rest::Base
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
@prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
13
|
+
@next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
14
|
+
|
15
|
+
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
16
|
+
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
17
|
+
|
18
|
+
sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
|
19
|
+
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)
|
20
|
+
|
21
|
+
@abandoned_checkout_url = T.let(nil, T.nilable(String))
|
22
|
+
@billing_address = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
23
|
+
@buyer_accepts_marketing = T.let(nil, T.nilable(T::Boolean))
|
24
|
+
@buyer_accepts_sms_marketing = T.let(nil, T.nilable(T::Boolean))
|
25
|
+
@cart_token = T.let(nil, T.nilable(String))
|
26
|
+
@closed_at = T.let(nil, T.nilable(String))
|
27
|
+
@completed_at = T.let(nil, T.nilable(String))
|
28
|
+
@created_at = T.let(nil, T.nilable(String))
|
29
|
+
@currency = T.let(nil, T.nilable(Currency))
|
30
|
+
@customer = T.let(nil, T.nilable(Customer))
|
31
|
+
@customer_locale = T.let(nil, T.nilable(String))
|
32
|
+
@device_id = T.let(nil, T.nilable(Integer))
|
33
|
+
@discount_codes = T.let(nil, T.nilable(T::Array[T.untyped]))
|
34
|
+
@email = T.let(nil, T.nilable(String))
|
35
|
+
@gateway = T.let(nil, T.nilable(String))
|
36
|
+
@id = T.let(nil, T.nilable(Integer))
|
37
|
+
@landing_site = T.let(nil, T.nilable(String))
|
38
|
+
@line_items = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
39
|
+
@location_id = T.let(nil, T.nilable(Integer))
|
40
|
+
@note = T.let(nil, T.nilable(String))
|
41
|
+
@phone = T.let(nil, T.nilable(String))
|
42
|
+
@presentment_currency = T.let(nil, T.nilable(String))
|
43
|
+
@referring_site = T.let(nil, T.nilable(String))
|
44
|
+
@shipping_address = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
45
|
+
@shipping_lines = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
46
|
+
@sms_marketing_phone = T.let(nil, T.nilable(String))
|
47
|
+
@source_name = T.let(nil, T.nilable(String))
|
48
|
+
@subtotal_price = T.let(nil, T.nilable(String))
|
49
|
+
@tax_lines = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
50
|
+
@taxes_included = T.let(nil, T.nilable(T::Boolean))
|
51
|
+
@token = T.let(nil, T.nilable(String))
|
52
|
+
@total_discounts = T.let(nil, T.nilable(String))
|
53
|
+
@total_duties = T.let(nil, T.nilable(String))
|
54
|
+
@total_line_items_price = T.let(nil, T.nilable(String))
|
55
|
+
@total_price = T.let(nil, T.nilable(String))
|
56
|
+
@total_tax = T.let(nil, T.nilable(String))
|
57
|
+
@total_weight = T.let(nil, T.nilable(Integer))
|
58
|
+
@updated_at = T.let(nil, T.nilable(String))
|
59
|
+
@user_id = T.let(nil, T.nilable(Integer))
|
60
|
+
|
61
|
+
super(session: session, from_hash: from_hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
@has_one = T.let({
|
65
|
+
currency: Currency,
|
66
|
+
customer: Customer
|
67
|
+
}, T::Hash[Symbol, Class])
|
68
|
+
@has_many = T.let({
|
69
|
+
discount_codes: DiscountCode
|
70
|
+
}, T::Hash[Symbol, Class])
|
71
|
+
@paths = T.let([
|
72
|
+
{http_method: :get, operation: :checkouts, ids: [], path: "checkouts.json"},
|
73
|
+
{http_method: :get, operation: :checkouts, ids: [], path: "checkouts.json"}
|
74
|
+
], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]])
|
75
|
+
|
76
|
+
sig { returns(T.nilable(String)) }
|
77
|
+
attr_reader :abandoned_checkout_url
|
78
|
+
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
79
|
+
attr_reader :billing_address
|
80
|
+
sig { returns(T.nilable(T::Boolean)) }
|
81
|
+
attr_reader :buyer_accepts_marketing
|
82
|
+
sig { returns(T.nilable(T::Boolean)) }
|
83
|
+
attr_reader :buyer_accepts_sms_marketing
|
84
|
+
sig { returns(T.nilable(String)) }
|
85
|
+
attr_reader :cart_token
|
86
|
+
sig { returns(T.nilable(String)) }
|
87
|
+
attr_reader :closed_at
|
88
|
+
sig { returns(T.nilable(String)) }
|
89
|
+
attr_reader :completed_at
|
90
|
+
sig { returns(T.nilable(String)) }
|
91
|
+
attr_reader :created_at
|
92
|
+
sig { returns(T.nilable(Currency)) }
|
93
|
+
attr_reader :currency
|
94
|
+
sig { returns(T.nilable(Customer)) }
|
95
|
+
attr_reader :customer
|
96
|
+
sig { returns(T.nilable(String)) }
|
97
|
+
attr_reader :customer_locale
|
98
|
+
sig { returns(T.nilable(Integer)) }
|
99
|
+
attr_reader :device_id
|
100
|
+
sig { returns(T.nilable(T::Array[DiscountCode])) }
|
101
|
+
attr_reader :discount_codes
|
102
|
+
sig { returns(T.nilable(String)) }
|
103
|
+
attr_reader :email
|
104
|
+
sig { returns(T.nilable(String)) }
|
105
|
+
attr_reader :gateway
|
106
|
+
sig { returns(T.nilable(Integer)) }
|
107
|
+
attr_reader :id
|
108
|
+
sig { returns(T.nilable(String)) }
|
109
|
+
attr_reader :landing_site
|
110
|
+
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
111
|
+
attr_reader :line_items
|
112
|
+
sig { returns(T.nilable(Integer)) }
|
113
|
+
attr_reader :location_id
|
114
|
+
sig { returns(T.nilable(String)) }
|
115
|
+
attr_reader :note
|
116
|
+
sig { returns(T.nilable(String)) }
|
117
|
+
attr_reader :phone
|
118
|
+
sig { returns(T.nilable(String)) }
|
119
|
+
attr_reader :presentment_currency
|
120
|
+
sig { returns(T.nilable(String)) }
|
121
|
+
attr_reader :referring_site
|
122
|
+
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
123
|
+
attr_reader :shipping_address
|
124
|
+
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
125
|
+
attr_reader :shipping_lines
|
126
|
+
sig { returns(T.nilable(String)) }
|
127
|
+
attr_reader :sms_marketing_phone
|
128
|
+
sig { returns(T.nilable(String)) }
|
129
|
+
attr_reader :source_name
|
130
|
+
sig { returns(T.nilable(String)) }
|
131
|
+
attr_reader :subtotal_price
|
132
|
+
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
133
|
+
attr_reader :tax_lines
|
134
|
+
sig { returns(T.nilable(T::Boolean)) }
|
135
|
+
attr_reader :taxes_included
|
136
|
+
sig { returns(T.nilable(String)) }
|
137
|
+
attr_reader :token
|
138
|
+
sig { returns(T.nilable(String)) }
|
139
|
+
attr_reader :total_discounts
|
140
|
+
sig { returns(T.nilable(String)) }
|
141
|
+
attr_reader :total_duties
|
142
|
+
sig { returns(T.nilable(String)) }
|
143
|
+
attr_reader :total_line_items_price
|
144
|
+
sig { returns(T.nilable(String)) }
|
145
|
+
attr_reader :total_price
|
146
|
+
sig { returns(T.nilable(String)) }
|
147
|
+
attr_reader :total_tax
|
148
|
+
sig { returns(T.nilable(Integer)) }
|
149
|
+
attr_reader :total_weight
|
150
|
+
sig { returns(T.nilable(String)) }
|
151
|
+
attr_reader :updated_at
|
152
|
+
sig { returns(T.nilable(Integer)) }
|
153
|
+
attr_reader :user_id
|
154
|
+
|
155
|
+
class << self
|
156
|
+
sig do
|
157
|
+
params(
|
158
|
+
since_id: T.untyped,
|
159
|
+
created_at_min: T.untyped,
|
160
|
+
created_at_max: T.untyped,
|
161
|
+
updated_at_min: T.untyped,
|
162
|
+
updated_at_max: T.untyped,
|
163
|
+
status: T.untyped,
|
164
|
+
limit: T.untyped,
|
165
|
+
session: Auth::Session,
|
166
|
+
kwargs: T.untyped
|
167
|
+
).returns(T.untyped)
|
168
|
+
end
|
169
|
+
def checkouts(
|
170
|
+
since_id: nil,
|
171
|
+
created_at_min: nil,
|
172
|
+
created_at_max: nil,
|
173
|
+
updated_at_min: nil,
|
174
|
+
updated_at_max: nil,
|
175
|
+
status: nil,
|
176
|
+
limit: nil,
|
177
|
+
session: ShopifyAPI::Context.active_session,
|
178
|
+
**kwargs
|
179
|
+
)
|
180
|
+
request(
|
181
|
+
http_method: :get,
|
182
|
+
operation: :checkouts,
|
183
|
+
session: session,
|
184
|
+
ids: {},
|
185
|
+
params: {since_id: since_id, created_at_min: created_at_min, created_at_max: created_at_max, updated_at_min: updated_at_min, updated_at_max: updated_at_max, status: status, limit: limit}.merge(kwargs).compact,
|
186
|
+
body: {},
|
187
|
+
entity: nil,
|
188
|
+
)
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
########################################################################################################################
|
5
|
+
# This file is auto-generated. If you have an issue, please create a GitHub issue. #
|
6
|
+
########################################################################################################################
|
7
|
+
|
8
|
+
module ShopifyAPI
|
9
|
+
class AccessScope < ShopifyAPI::Rest::Base
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
@prev_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
13
|
+
@next_page_info = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
14
|
+
|
15
|
+
@api_call_limit = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
16
|
+
@retry_request_after = T.let(Concurrent::ThreadLocalVar.new { nil }, Concurrent::ThreadLocalVar)
|
17
|
+
|
18
|
+
sig { params(session: T.nilable(ShopifyAPI::Auth::Session), from_hash: T.nilable(T::Hash[T.untyped, T.untyped])).void }
|
19
|
+
def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil)
|
20
|
+
|
21
|
+
@handle = T.let(nil, T.nilable(String))
|
22
|
+
@access_scopes = T.let(nil, T.nilable(T::Array[T.untyped]))
|
23
|
+
|
24
|
+
super(session: session, from_hash: from_hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
@has_one = T.let({}, T::Hash[Symbol, Class])
|
28
|
+
@has_many = T.let({}, T::Hash[Symbol, Class])
|
29
|
+
@custom_prefix = T.let("/admin/oauth", T.nilable(String))
|
30
|
+
@paths = T.let([
|
31
|
+
{http_method: :get, operation: :get, ids: [], path: "access_scopes.json"}
|
32
|
+
], T::Array[T::Hash[String, T.any(T::Array[Symbol], String, Symbol)]])
|
33
|
+
|
34
|
+
sig { returns(T.nilable(String)) }
|
35
|
+
attr_reader :handle
|
36
|
+
sig { returns(T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])) }
|
37
|
+
attr_reader :access_scopes
|
38
|
+
|
39
|
+
class << self
|
40
|
+
sig do
|
41
|
+
params(
|
42
|
+
session: Auth::Session,
|
43
|
+
kwargs: T.untyped
|
44
|
+
).returns(T::Array[AccessScope])
|
45
|
+
end
|
46
|
+
def all(
|
47
|
+
session: ShopifyAPI::Context.active_session,
|
48
|
+
**kwargs
|
49
|
+
)
|
50
|
+
response = base_find(
|
51
|
+
session: session,
|
52
|
+
ids: {},
|
53
|
+
params: {}.merge(kwargs).compact,
|
54
|
+
)
|
55
|
+
|
56
|
+
T.cast(response, T::Array[AccessScope])
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|