shopify_api 8.1.0 → 9.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/README.md +22 -10
  4. data/docs/graphql.md +191 -0
  5. data/lib/shopify_api.rb +2 -0
  6. data/lib/shopify_api/api_version.rb +1 -1
  7. data/lib/shopify_api/graphql.rb +79 -0
  8. data/lib/shopify_api/graphql/http_client.rb +22 -0
  9. data/lib/shopify_api/graphql/railtie.rb +17 -0
  10. data/lib/shopify_api/graphql/task.rake +100 -0
  11. data/lib/shopify_api/resources/assigned_fulfillment_order.rb +16 -0
  12. data/lib/shopify_api/resources/base.rb +8 -0
  13. data/lib/shopify_api/resources/fulfillment.rb +34 -0
  14. data/lib/shopify_api/resources/fulfillment_order.rb +137 -0
  15. data/lib/shopify_api/resources/fulfillment_order_locations_for_move.rb +4 -0
  16. data/lib/shopify_api/resources/fulfillment_v2.rb +20 -0
  17. data/lib/shopify_api/resources/order.rb +7 -0
  18. data/lib/shopify_api/session.rb +3 -3
  19. data/lib/shopify_api/version.rb +1 -1
  20. data/test/assigned_fulfillment_order_test.rb +77 -0
  21. data/test/base_test.rb +14 -0
  22. data/test/fixtures/assigned_fulfillment_orders.json +78 -0
  23. data/test/fixtures/fulfillment_order.json +38 -0
  24. data/test/fixtures/fulfillment_order_locations_for_move.json +18 -0
  25. data/test/fixtures/fulfillment_orders.json +78 -0
  26. data/test/fixtures/fulfillments.json +53 -0
  27. data/test/fixtures/graphql/2019-10.json +1083 -0
  28. data/test/fixtures/graphql/dummy_schema.rb +16 -0
  29. data/test/fixtures/graphql/unstable.json +1083 -0
  30. data/test/fulfillment_order_test.rb +462 -0
  31. data/test/fulfillment_order_test_helper.rb +7 -0
  32. data/test/fulfillment_test.rb +164 -1
  33. data/test/fulfillment_v2_test.rb +62 -0
  34. data/test/graphql/http_client_test.rb +26 -0
  35. data/test/graphql_test.rb +147 -0
  36. data/test/order_test.rb +50 -0
  37. data/test/session_test.rb +26 -13
  38. data/test/test_helper.rb +4 -1
  39. metadata +25 -3
  40. data/lib/shopify_api/resources/graphql.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6b6467bc9d4c7e3180f0385a0b7c6f75921b391659e7d0566796fb34f5fe63d
4
- data.tar.gz: 6bcd7084be9a692d3e960c5659b4ed4e25abd629e6f91f58faa9934f55f720a1
3
+ metadata.gz: 20f844e10d85c5f8069b9cfa56f081b5396e247e3a3ed9db28ebdb41dcf8ba54
4
+ data.tar.gz: fb4f49e89c1147b86674c98e02d4fb7b3338cc834cbb4f1e0dfe0953500db6a8
5
5
  SHA512:
6
- metadata.gz: 4ab61dddb0c4fe44d89c9bced7e328c8e76932c9876803d2aeef6ea0a7c9eace8eaa728841f9445a2c59e53b555d8c16c40b4ed881b8630069f2e8343c2f3971
7
- data.tar.gz: 5808669808de2b254c33d2a0d4f120f9b47a4868bb992575824f2a9348ecd74a1c54231b3ed821e694de1c2ed63ae6653c5138b6e86874d88c2a10fcee080965
6
+ metadata.gz: b2d726f0c5526c4800fe7b1fd260a27ac75f70251f879e179d10c093378aa6ebcce5dcf1a222a33c1f3dc27b5d547b3bb7269c25515c0000b6d311d3cccfd93b
7
+ data.tar.gz: 13c44be9e5ec956fd0f7d88b79d55460742cc0624f9c711fbf471de4907741f8d14ae7687307daca333e65988b0d0c84ffb0c49ced4ede0f56833f69da71e834
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ == Version 9.0.0
2
+
3
+ * Breaking change: Improved GraphQL client [#672](https://github.com/Shopify/shopify_api/pull/672). See the [client docs](docs/graphql.md) for usage and a migration guide.
4
+
5
+ * Added options hash to create_permission_url and makes redirect_uri required [#670](https://github.com/Shopify/shopify_api/pull/670)
6
+
7
+ * Release new Endpoint `fulfillment_order.locations_for_move` in 2020-01 REST API version [#669](https://github.com/Shopify/shopify_api/pull/669)
8
+
9
+ * Release new Endpoints for `fulfillment` in 2020-01 REST API version [#639](https://github.com/Shopify/shopify_api/pull/639):
10
+ * `fulfillment.create` with `line_items_by_fulfillment_order`
11
+ * `fulfillment.update_tracking`
12
+ * `fulfillment.cancel`
13
+
14
+ * Release new Endpoints for `fulfillment_order` in 2020-01 REST API version [#637](https://github.com/Shopify/shopify_api/pull/637):
15
+ * `fulfillment_order.fulfillment_request`
16
+ * `fulfillment_order.fulfillment_request.accept`
17
+ * `fulfillment_order.fulfillment_request.reject`
18
+ * `fulfillment_order.cancellation_request`
19
+ * `fulfillment_order.cancellation_request.accept`
20
+ * `fulfillment_order.cancellation_request.reject`
21
+
22
+ * Release new Endpoints `fulfillment_order.move`, `fulfillment_order.cancel` and `fulfillment_order.close` in 2020-01 REST API version [#635](https://github.com/Shopify/shopify_api/pull/635)
23
+
24
+ * Release new Endpoint `order.fulfillment_orders`, and active resources `AssignedFulfillmentOrder` and `FulfillmentOrder` in 2020-01 REST API version [#633](https://github.com/Shopify/shopify_api/pull/633)
25
+
1
26
  == Version 8.1.0
2
27
 
3
28
  * Release 2020-01 REST ADMIN API VERSION [#656](https://github.com/Shopify/shopify_api/pull/656)
data/README.md CHANGED
@@ -192,17 +192,16 @@ ShopifyAPI uses ActiveResource to communicate with the REST web service. ActiveR
192
192
  shopify_session = ShopifyAPI::Session.new(domain: "SHOP_NAME.myshopify.com", api_version: api_version, token: nil)
193
193
  ```
194
194
 
195
- Then call:
195
+ Then call `create_permission_url` with the redirect_uri you've registered for your application:
196
196
 
197
197
  ```ruby
198
- scope = ["write_products"]
199
- permission_url = shopify_session.create_permission_url(scope)
198
+ permission_url = shopify_session.create_permission_url(scope, "https://my_redirect_uri.com")
200
199
  ```
201
200
 
202
- or if you want a custom redirect_uri:
201
+ You can also pass a state parameter in the options hash as a last argument:
203
202
 
204
203
  ```ruby
205
- permission_url = shopify_session.create_permission_url(scope, "https://my_redirect_uri.com")
204
+ permission_url = shopify_session.create_permission_url(scope, "https://my_redirect_uri.com", { state: "My Nonce" })
206
205
  ```
207
206
 
208
207
  4. Once authorized, the shop redirects the owner to the return URL of your application with a parameter named 'code'. This is a temporary token that the app can exchange for a permanent access token.
@@ -347,15 +346,18 @@ gem install shopify_api_console
347
346
 
348
347
  ## GraphQL
349
348
 
350
- This library also supports Shopify's new [GraphQL API](https://help.shopify.com/api/graphql-admin-api)
351
- via a dependency on the [graphql-client](https://github.com/github/graphql-client) gem.
349
+ Note: the GraphQL client has improved and changed in version 9.0. See the [client documentation](docs/graphql.md)
350
+ for full usage details and a [migration guide](docs/graphql.md#migration-guide).
351
+
352
+ This library also supports Shopify's [GraphQL Admin API](https://help.shopify.com/api/graphql-admin-api)
353
+ via integration with the [graphql-client](https://github.com/github/graphql-client) gem.
352
354
  The authentication process (steps 1-5 under [Getting Started](#getting-started))
353
- is identical. Once your session is activated, simply construct a new graphql
354
- client and use `parse` and `query` as defined by
355
+ is identical. Once your session is activated, simply access the GraphQL client
356
+ and use `parse` and `query` as defined by
355
357
  [graphql-client](https://github.com/github/graphql-client#defining-queries).
356
358
 
357
359
  ```ruby
358
- client = ShopifyAPI::GraphQL.new
360
+ client = ShopifyAPI::GraphQL.client
359
361
 
360
362
  SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
361
363
  {
@@ -369,6 +371,8 @@ result = client.query(SHOP_NAME_QUERY)
369
371
  result.data.shop.name
370
372
  ```
371
373
 
374
+ [GraphQL client documentation](docs/graphql.md)
375
+
372
376
  ## Threadsafety
373
377
 
374
378
  ActiveResource is threadsafe as of version 4.1 (which works with Rails 4.x and above).
@@ -403,6 +407,14 @@ while products.next_page?
403
407
  end
404
408
  ```
405
409
 
410
+ If you want cursor based pagination to work across page loads, or want to distribute workload across multiple background jobs, you can use #next_page_info or #previous_page_info methods that return strings:
411
+
412
+ ```
413
+ first_batch_products = ShopifyAPI::Product.find(:all, params: { limit: 50 })
414
+ second_batch_products = ShopifyAPI::Product.find(:all, params: { limit: 50, page_info: first_batch_products.next_page_info })
415
+ ...
416
+ ```
417
+
406
418
  Relative cursor pagination is currently available for all endpoints using the `2019-10` and later API versions.
407
419
 
408
420
  ## Using Development Version
data/docs/graphql.md ADDED
@@ -0,0 +1,191 @@
1
+ # GraphQL client
2
+
3
+ The `shopify_api` gem includes a full featured GraphQL client to interact with
4
+ Shopify's [GraphQL Admin API](https://help.shopify.com/en/api/graphql-admin-api).
5
+ GitHub's [graphql-client](https://github.com/github/graphql-client) is used as
6
+ the underlying client and this library integrates it with our existing
7
+ session, authentication, and API versioning features.
8
+
9
+ ## Example
10
+
11
+ ```ruby
12
+ client = ShopifyAPI::GraphQL.client
13
+
14
+ SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
15
+ {
16
+ shop {
17
+ name
18
+ }
19
+ }
20
+ GRAPHQL
21
+
22
+ result = client.query(SHOP_NAME_QUERY)
23
+ result.data.shop.name
24
+ ```
25
+
26
+ * [Getting started](#getting-started)
27
+ * [Rails integration](#rails-integration)
28
+ * [API versioning](#api-versioning)
29
+ * [Initialization process](#initialization-process)
30
+ * [Migration guide](#migration-guide)
31
+
32
+ ## Getting started
33
+
34
+ 1. [Dump the schema](#dump-the-schema)
35
+ 2. [Configure session/authencation](#sessions-and-authentication)
36
+ 3. [Make queries](#make-queries)
37
+
38
+ ### Dump the schema
39
+ One of the main benefits of GraphQL is its [schema and type system](https://graphql.org/learn/schema/)
40
+ which enables tools like graphql-client to ensure your queries are valid in development.
41
+
42
+ So the first step in making GraphQL queries is having a local JSON file of Shopify's Admin schema.
43
+ This gem provides a `shopify_api:graphql:dump` Rake task to make it as easy as possible:
44
+
45
+ ```bash
46
+ $ rake shopify_api:graphql:dump SHOP_URL="https://API_KEY:PASSWORD@SHOP_NAME.myshopify.com" API_VERSION=2020-01
47
+ ```
48
+
49
+ If successful `db/shopify_graphql_schemas/2020-01.json` will be created.
50
+
51
+ You can either use private app authentication or an OAuth access token. Run `rake shopify_api:graphql:dump`
52
+ to see full usage details.
53
+
54
+ If you're using shopify_api in a Rails app, the default location for schema files is `db/shopify_graphql_schemas`.
55
+ For non-Rails applications, the default is `shopify_graphql_schemas` in your project root.
56
+
57
+ The schema path location can be changed via `ShopifyAPI::GraphQL.schema_location`:
58
+
59
+ ```ruby
60
+ ShopifyAPI::GraphQL.schema_location = 'assets/schemas'
61
+ ```
62
+
63
+ #### Updating schemas
64
+ Each time you want to use a new API version, or update an existing one
65
+ (such as the `unstable` version), simply run the Rake task again to overwrite the file.
66
+
67
+ ### Sessions and authentication
68
+ The GraphQL client is designed to be integrated with the rest of shopify_api so
69
+ all its features such as sessions, authentication, and API versioning work the
70
+ exact same.
71
+
72
+ If you've already been using the shopify_api gem in your application to make
73
+ REST API calls then no other configuration is necessary.
74
+
75
+ Steps 1-5 of our main [Getting started](https://github.com/Shopify/shopify_api#getting-started)
76
+ section still apply for the GraphQL client as well.
77
+
78
+ ### Make queries
79
+ Now that you've dumped a schema file and configured an authenticated session, you can make GraphQL API requests.
80
+ graphql-client encourages all queries to be defined statically as constants:
81
+
82
+ ```ruby
83
+ SHOP_NAME_QUERY = ShopifyAPI::GraphQL.client.parse <<-'GRAPHQL'
84
+ {
85
+ shop {
86
+ name
87
+ }
88
+ }
89
+ GRAPHQL
90
+
91
+ result = ShopifyAPI::GraphQL.client.query(SHOP_NAME_QUERY)
92
+ result.data.shop.name
93
+ ```
94
+
95
+ But we've also enabled its `allow_dynamic_queries` option if you prefer:
96
+
97
+ ```ruby
98
+ query = ShopifyAPI::GraphQL.client.parse <<-'GRAPHQL'
99
+ {
100
+ shop {
101
+ name
102
+ }
103
+ }
104
+ GRAPHQL
105
+
106
+ result = ShopifyAPI::GraphQL.client.query(query)
107
+ result.data.shop.name
108
+ ```
109
+
110
+ See the [graphql-client documentation](https://github.com/github/graphql-client#defining-queries)
111
+ for more details on defining and executing queries.
112
+
113
+ ## Rails integration
114
+ `ShopifyAPI::GraphQL` integrates with Rails to automatically do the following:
115
+
116
+ * load the `shopify_api:graphql:dump` Rake task
117
+ * set the `schema_location` to be in the `db` directory in your Rails root
118
+ * initialize clients in the Rails app initializer phase
119
+
120
+ ## API versioning
121
+ `ShopifyAPI::GraphQL` is version aware and lets you easily make queries to multiple
122
+ API versions through version specific clients if need be.
123
+
124
+ If you have multiple clients and need to be explicit you can specify the version parameter:
125
+
126
+ ```ruby
127
+ ShopifyAPI::GraphQL.client # defaults to the client using ShopifyAPI::Base.api_version
128
+ ShopifyAPI::GraphQL.client('unstable')
129
+ ```
130
+
131
+ ## Initialization process
132
+ `ShopifyAPI::GraphQL` is a thin integration layer which initializes `GraphQL::Client`s
133
+ from local schema files.
134
+
135
+ `ShopifyAPI::GraphQL.initialize_clients` scans `ShopifyAPI::GraphQL.schema_location`
136
+ and creates a client for each version specific schema file found.
137
+
138
+ This happens automatically in a Rails application due to our [integration](#rails-integration).
139
+ For non-Rails applications, ensure you call `ShopifyAPI::GraphQL.initialize_clients`
140
+ during your boot process.
141
+
142
+ The goal is to have all clients created at boot so there's no schema loading,
143
+ parsing, or client instantiation done during runtime when your app serves a request.
144
+
145
+ ## Migration guide
146
+ Prior to shopify_api v9.0 the GraphQL client implementation was limited and almost
147
+ unusable due to the client making dynamic introspection queries to Shopify's API.
148
+ This was not only very slow but also led to unbounded memory growth.
149
+
150
+ There are two steps to migrate to the new client:
151
+ 1. [Dump a local schema file](#dump-the-schema)
152
+ 2. [Migrate `client` usage](#migrate-usage)
153
+
154
+ ### Migrate usage
155
+
156
+ Previously a client was initialized with `ShopifyAPI::GraphQL.new`:
157
+ ```ruby
158
+ client = ShopifyAPI::GraphQL.new
159
+
160
+ SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
161
+ {
162
+ shop {
163
+ name
164
+ }
165
+ }
166
+ GRAPHQL
167
+
168
+ result = client.query(SHOP_NAME_QUERY)
169
+ result.data.shop.name
170
+ ```
171
+
172
+ Now there's no need to initialize a client so all references to
173
+ `ShopifyAPI::GraphQL.new` should be removed and instead the client is called
174
+ via `ShopifyAPI::GraphQL.client`:
175
+
176
+ ```ruby
177
+ client = ShopifyAPI::GraphQL.client
178
+
179
+ SHOP_NAME_QUERY = client.parse <<-'GRAPHQL'
180
+ {
181
+ shop {
182
+ name
183
+ }
184
+ }
185
+ GRAPHQL
186
+
187
+ result = client.query(SHOP_NAME_QUERY)
188
+ result.data.shop.name
189
+ ```
190
+
191
+ See [making queries](#making-queries) for more usage details.
data/lib/shopify_api.rb CHANGED
@@ -23,6 +23,8 @@ require 'shopify_api/session'
23
23
  require 'shopify_api/message_enricher'
24
24
  require 'shopify_api/connection'
25
25
  require 'shopify_api/pagination_link_headers'
26
+ require 'shopify_api/graphql'
27
+ require 'shopify_api/graphql/railtie' if defined?(Rails)
26
28
 
27
29
  if ShopifyAPI::Base.respond_to?(:connection_class)
28
30
  ShopifyAPI::Base.connection_class = ShopifyAPI::Connection
@@ -5,8 +5,8 @@ module ShopifyAPI
5
5
  class ApiVersionNotSetError < StandardError; end
6
6
  include Comparable
7
7
 
8
- HANDLE_FORMAT = /^\d{4}-\d{2}$/.freeze
9
8
  UNSTABLE_HANDLE = 'unstable'
9
+ HANDLE_FORMAT = /((\d{4}-\d{2})|#{UNSTABLE_HANDLE})/.freeze
10
10
  UNSTABLE_AS_DATE = Time.utc(3000, 1, 1)
11
11
  API_PREFIX = '/admin/api/'
12
12
  LOOKUP_MODES = [:raise_on_unknown, :define_on_unknown].freeze
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ require 'graphql/client'
3
+ require 'shopify_api/graphql/http_client'
4
+
5
+ module ShopifyAPI
6
+ module GraphQL
7
+ DEFAULT_SCHEMA_LOCATION_PATH = Pathname('shopify_graphql_schemas')
8
+
9
+ InvalidSchema = Class.new(StandardError)
10
+ InvalidClient = Class.new(StandardError)
11
+
12
+ class << self
13
+ delegate :parse, :query, to: :client
14
+
15
+ def client(api_version = ShopifyAPI::Base.api_version.handle)
16
+ initialize_client_cache
17
+ cached_client = @_client_cache[api_version]
18
+
19
+ if cached_client
20
+ cached_client
21
+ else
22
+ schema_file = schema_location.join("#{api_version}.json")
23
+
24
+ if !schema_file.exist?
25
+ raise InvalidClient, <<~MSG
26
+ Client for API version #{api_version} does not exist because no schema file exists at `#{schema_file}`.
27
+
28
+ To dump the schema file, use the `rake shopify_api:graphql:dump` task.
29
+ MSG
30
+ else
31
+ puts '[WARNING] Client was not pre-initialized. Ensure `ShopifyAPI::GraphQL.initialize_clients` is called during app initialization.'
32
+ initialize_clients
33
+ @_client_cache[api_version]
34
+ end
35
+ end
36
+ end
37
+
38
+ def clear_clients
39
+ @_client_cache = {}
40
+ end
41
+
42
+ def initialize_clients
43
+ initialize_client_cache
44
+
45
+ Dir.glob(schema_location.join("*.json")).each do |schema_file|
46
+ schema_file = Pathname(schema_file)
47
+ matches = schema_file.basename.to_s.match(/^#{ShopifyAPI::ApiVersion::HANDLE_FORMAT}\.json$/)
48
+
49
+ if matches
50
+ api_version = ShopifyAPI::ApiVersion.new(handle: matches[1])
51
+ else
52
+ raise InvalidSchema, "Invalid schema file name `#{schema_file}`. Does not match format of: `<version>.json`."
53
+ end
54
+
55
+ schema = ::GraphQL::Client.load_schema(schema_file.to_s)
56
+ client = ::GraphQL::Client.new(schema: schema, execute: HTTPClient.new(api_version)).tap do |c|
57
+ c.allow_dynamic_queries = true
58
+ end
59
+
60
+ @_client_cache[api_version.handle] = client
61
+ end
62
+ end
63
+
64
+ def schema_location
65
+ @schema_location || DEFAULT_SCHEMA_LOCATION_PATH
66
+ end
67
+
68
+ def schema_location=(path)
69
+ @schema_location = Pathname(path)
70
+ end
71
+
72
+ private
73
+
74
+ def initialize_client_cache
75
+ @_client_cache ||= {}
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require 'graphql/client/http'
3
+
4
+ module ShopifyAPI
5
+ module GraphQL
6
+ class HTTPClient < ::GraphQL::Client::HTTP
7
+ def initialize(api_version)
8
+ @api_version = api_version
9
+ end
10
+
11
+ def headers(_context)
12
+ ShopifyAPI::Base.headers
13
+ end
14
+
15
+ def uri
16
+ ShopifyAPI::Base.site.dup.tap do |uri|
17
+ uri.path = @api_version.construct_graphql_path
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/railtie'
3
+
4
+ module ShopifyAPI
5
+ module GraphQL
6
+ class Railtie < Rails::Railtie
7
+ initializer 'shopify_api.initialize_graphql_clients' do |app|
8
+ ShopifyAPI::GraphQL.schema_location = app.root.join('db', ShopifyAPI::GraphQL.schema_location)
9
+ ShopifyAPI::GraphQL.initialize_clients
10
+ end
11
+
12
+ rake_tasks do
13
+ load 'shopify_api/graphql/task.rake'
14
+ end
15
+ end
16
+ end
17
+ end