lucid-shopify 0.57.0 → 0.60.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c1fdb1699b2550ff42c9e707fca8a483281910066b4454c0a46472325f9ac9b
4
- data.tar.gz: fb18e1fa4164cc1753bdeae3cb6c7b7c1120fe4437faf8d270ca9b3e547a4e61
3
+ metadata.gz: a2000b0881f8f32a1ae28c1e81f75c4e10c99fb386e090087c930835453a7c20
4
+ data.tar.gz: 871ddf85d10f96e5f03c253436d4fb49f76da54ed0e1bca68428fa2ae4cd095e
5
5
  SHA512:
6
- metadata.gz: 1bcf5b74b4bd51e62df057b9280208439a376109497845794dddc78d2523ce92cc5f7dedc7385e1fc61d74d234f39db0c71f41fdeb8a3e6a98fc0a27f7788f0a
7
- data.tar.gz: 78c4ec7ec01c43338f17fc5b25129285e2a28371da9afb20c8bedbd47ec5ae5379954d66f834b4740159d69a32110cfc48f27e7eabab101a6fca36e4ac260ac8
6
+ metadata.gz: 17cbb58d6af88ebe841ff7ad8d2217512a7a11c78f40763a9061ef85e4e132d0334cce964235eed844d7d231a2657107ac3a493a091b6b2aba360d8f4042f925
7
+ data.tar.gz: bfebbb6f59bb8592827a6b3264c00bef9d3839e9236628d4e8eb91da30af9cc032e15500d389f366d4c8c7b967b8bbe75612d28d3de8658e3124fc13d1495661
data/README.md CHANGED
@@ -101,9 +101,7 @@ Since API version 2019-10, Shopify has offered an API for bulk requests (using
101
101
  the GraphQL API). The gem wraps this API, by writing the result to a temporary
102
102
  file and yielding each line of the result to limit memory usage.
103
103
 
104
- client.bulk(credentials).around do |&y|
105
- db.transaction { y.() }
106
- end.(<<~QUERY) do |product|
104
+ client.bulk(credentials, <<~QUERY) do |products|
107
105
  {
108
106
  products {
109
107
  edges {
@@ -115,12 +113,19 @@ file and yielding each line of the result to limit memory usage.
115
113
  }
116
114
  }
117
115
  QUERY
118
- db[:products].insert(
119
- id: product['id'],
120
- handle: product['handle'],
121
- )
116
+ db.transaction do
117
+ products.each do |product|
118
+ db[:products].insert(
119
+ id: product['id'],
120
+ handle: product['handle'],
121
+ )
122
+ end
123
+ end
122
124
  end
123
125
 
126
+ Bulk requests are limited to one per shop at any one time. Calling a new bulk
127
+ request via lucid-shopify will cancel any request in progress for the shop.
128
+
124
129
 
125
130
  ### Pagination
126
131
 
@@ -19,59 +19,35 @@ module Lucid
19
19
  param :client
20
20
  # @return [Credentials]
21
21
  param :credentials
22
+ # @return [String]
23
+ param :id
22
24
  end
23
25
 
24
- # Set a block which is called after the file is downloaded, and around
25
- # the line by line iteration in {#call}. The block must yield control
26
- # back to the caller.
26
+ # Wait for the operation to complete, then download the JSONL result
27
+ # data which is yielded as an {Enumerator} to the block. The data is
28
+ # streamed and parsed line by line to limit memory usage.
27
29
  #
28
- # @return [self]
29
- #
30
- # @example
31
- # operation.around do |&y|
32
- # puts 'Before iteration'
33
- # y.()
34
- # ensure
35
- # puts 'After iteration'
36
- # end
37
- def around(&block)
38
- @around = block
39
-
40
- self
41
- end
42
-
43
- # Call the block set by {#around}.
44
- #
45
- # @yield inside the wrapper block
46
- private def call_around(&block)
47
- @around.respond_to?(:call) ? @around.(&block) : block.()
48
- end
49
-
50
- # @param query [String] the GraphQL query
51
30
  # @param delay [Integer] delay between polling requests in seconds
52
31
  # @param http [HTTP::Client]
53
32
  #
54
- # @yield [Hash] each parsed line of JSONL (streamed to limit memory usage)
55
- def call(query, delay: 1, http: Container[:http], &block)
56
- id = client.post_graphql(credentials, <<~QUERY)['data']['bulkOperationRunQuery']['bulkOperation']['id']
57
- mutation {
58
- bulkOperationRunQuery(
59
- query: """
60
- #{query}
61
- """
62
- ) {
63
- bulkOperation {
64
- id
65
- }
66
- userErrors {
67
- field
68
- message
69
- }
70
- }
71
- }
72
- QUERY
73
-
74
- url = poll(id, delay: delay)
33
+ # @yield [Enumerator<Hash>] yields each parsed line of JSONL
34
+ def call(delay: 1, http: Container[:http], &block)
35
+ url = loop do
36
+ status, url = poll
37
+
38
+ case status
39
+ when 'CANCELED'
40
+ raise CanceledOperationError
41
+ when 'EXPIRED'
42
+ raise ExpiredOperationError
43
+ when 'FAILED'
44
+ raise FailedOperationError
45
+ when 'COMPLETED'
46
+ break url
47
+ else
48
+ sleep(delay)
49
+ end
50
+ end
75
51
 
76
52
  return if url.nil?
77
53
 
@@ -84,22 +60,38 @@ module Lucid
84
60
  file.write(chunk)
85
61
  end
86
62
  file.rewind
87
- call_around do
88
- file.each_line do |line|
89
- block.(JSON.parse(line))
90
- end
91
- end
63
+ block.(Enumerator.new do |y|
64
+ file.each_line { |line| y << JSON.parse(line) }
65
+ end)
92
66
  ensure
93
67
  file.close
94
68
  file.unlink
95
69
  end
96
70
  end
97
71
 
98
- # @param id [Integer] of the bulk operation
99
- # @param delay [Integer]
100
- #
101
- # @return [String, nil] the download URL, or nil if the result data is empty
102
- private def poll(id, delay:)
72
+ # Cancel the bulk operation.
73
+ def cancel
74
+ client.post_graphql(credentials, <<~QUERY)
75
+ mutation {
76
+ bulkOperationCancel(id: "#{id}") {
77
+ userErrors {
78
+ field
79
+ message
80
+ }
81
+ }
82
+ }
83
+ QUERY
84
+
85
+ loop do
86
+ status, _ = poll
87
+
88
+ break unless status == 'CANCELING'
89
+ end
90
+ end
91
+
92
+ # @return [Array(String, String | nil)] the operation status and the
93
+ # download URL, or nil if the result data is empty
94
+ private def poll
103
95
  op = client.post_graphql(credentials, <<~QUERY)['data']['currentBulkOperation']
104
96
  {
105
97
  currentBulkOperation {
@@ -112,32 +104,23 @@ module Lucid
112
104
 
113
105
  raise ObsoleteOperationError if op['id'] != id
114
106
 
115
- case op['status']
116
- when 'CANCELED'
117
- raise CanceledOperationError
118
- when 'EXPIRED'
119
- raise ExpiredOperationError
120
- when 'FAILED'
121
- raise FailedOperationError
122
- when 'COMPLETED'
123
- op['url']
124
- else
125
- sleep(delay)
126
-
127
- poll(id, delay: delay)
128
- end
107
+ [
108
+ op['status'],
109
+ op['url'],
110
+ ]
129
111
  end
130
112
  end
131
113
 
114
+ # Create and start a new bulk operation via the GraphQL API.
115
+ #
132
116
  # @param client [Client]
133
117
  # @param credentials [Credentials]
118
+ # @param query [String] the GraphQL query
134
119
  #
135
120
  # @return [Operation]
136
121
  #
137
122
  # @example
138
- # bulk_request.(client, credentials).around do |&y|
139
- # db.transaction { y.() }
140
- # end.(<<~QUERY) do |product|
123
+ # bulk_request.(client, credentials, <<~QUERY).() do |products|
141
124
  # {
142
125
  # products {
143
126
  # edges {
@@ -149,15 +132,49 @@ module Lucid
149
132
  # }
150
133
  # }
151
134
  # QUERY
152
- # db[:products].insert(
153
- # id: product['id'],
154
- # handle: product['handle'],
155
- # )
135
+ # db.transaction do
136
+ # products.each do |product|
137
+ # db[:products].insert(
138
+ # id: product['id'],
139
+ # handle: product['handle'],
140
+ # )
141
+ # end
142
+ # end
156
143
  # end
157
- def call(client, credentials)
144
+ def call(client, credentials, query)
158
145
  Shopify.assert_api_version!('2019-10')
159
146
 
160
- Operation.new(client, credentials)
147
+ op = client.post_graphql(credentials, <<~QUERY)['data']['currentBulkOperation']
148
+ {
149
+ currentBulkOperation {
150
+ id
151
+ status
152
+ url
153
+ }
154
+ }
155
+ QUERY
156
+
157
+ Operation.new(client, credentials, op['id']).cancel if op && op['status'] == 'RUNNING'
158
+
159
+ id = client.post_graphql(credentials, <<~QUERY)['data']['bulkOperationRunQuery']['bulkOperation']['id']
160
+ mutation {
161
+ bulkOperationRunQuery(
162
+ query: """
163
+ #{query}
164
+ """
165
+ ) {
166
+ bulkOperation {
167
+ id
168
+ }
169
+ userErrors {
170
+ field
171
+ message
172
+ }
173
+ }
174
+ }
175
+ QUERY
176
+
177
+ Operation.new(client, credentials, id)
161
178
  end
162
179
  end
163
180
  end
@@ -62,9 +62,15 @@ module Lucid
62
62
  AuthenticatedClient.new(self, credentials)
63
63
  end
64
64
 
65
+ # If called with a block, calls {BulkRequest::Operation#call} immediately,
66
+ # else, returns the {BulkRequest::Operation}.
67
+ #
65
68
  # @see BulkRequest#call
66
- def bulk(*args)
67
- @bulk_request.(self, *args)
69
+ # @see BulkRequest::Operation#call
70
+ def bulk(*args, &block)
71
+ op = @bulk_request.(self, *args)
72
+
73
+ block ? op.(&block) : op
68
74
  end
69
75
 
70
76
  # @see DeleteRequest#initialize
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lucid
4
4
  module Shopify
5
- VERSION = '0.57.0'
5
+ VERSION = '0.60.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucid-shopify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.57.0
4
+ version: 0.60.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kelsey Judson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-15 00:00:00.000000000 Z
11
+ date: 2020-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv