shopify_api 8.1.0 → 9.0.0

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