Skip to content

Commit 1cb72ff

Browse files
sshawclaude
andcommitted
Use a Result class instead of a Hash for Bulk#result
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b59e028 commit 1cb72ff

File tree

4 files changed

+86
-57
lines changed

4 files changed

+86
-57
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,12 @@ p operation.url
5454
# etc...
5555

5656
if operation.completed?
57-
operation.results[:data].each { }
58-
operation.results[:errors].each { }
59-
operation.results[:user_errors].each { }
57+
operation.results.each do |result|
58+
# Each element is a Hash with the appropriate response, if any
59+
result.data.each { }
60+
result.errors.each { }
61+
result.user_errors.each { }
62+
end
6063
end
6164
```
6265

lib/shopify_api/graphql/bulk/operation.rb

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
require "time"
44

5+
require_relative "operation/result"
6+
57
module ShopifyAPI
68
module GraphQL
79
module Bulk
@@ -58,40 +60,11 @@ def parse_results(url)
5860

5961
response.body.each_line do |line|
6062
row = JSON.parse(line)
61-
entry = { :line => row["__lineNumber"] }
62-
63-
if row.include?("errors")
64-
entry[:errors] = row["errors"].map { |e| { :message => e["message"] } }
65-
end
66-
67-
if row["data"]
68-
data = row["data"].values.first
69-
70-
if data
71-
errors = data["userErrors"] || []
72-
entry[:user_errors] = errors.map { |e| { :field => e["field"], :message => e["message"] } } if errors.any?
73-
entry[:data] = snake_case_keys(data.reject { |k, _| k == "userErrors" })
74-
end
75-
end
76-
77-
results << entry
63+
results << Operation::Result.new(row)
7864
end
7965

8066
results
8167
end
82-
83-
def snake_case_keys(object)
84-
case object
85-
when Hash
86-
object.each_with_object({}) do |(key, value), result|
87-
result[Strings::Case.snakecase(key).to_sym] = snake_case_keys(value)
88-
end
89-
when Array
90-
object.map { |value| snake_case_keys(value) }
91-
else
92-
object
93-
end
94-
end
9568
end
9669
end
9770
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
module ShopifyAPI
4+
module GraphQL
5+
module Bulk
6+
class Operation
7+
class Result
8+
attr_reader :line, :data, :errors, :user_errors
9+
10+
def initialize(row)
11+
@errors = []
12+
@user_errors = []
13+
14+
@line = row["__lineNumber"]
15+
16+
if row.include?("errors")
17+
@errors = row["errors"].map { |e| { :message => e["message"] } }
18+
end
19+
20+
if row["data"]
21+
data = row["data"].values.first
22+
if data
23+
errors = data["userErrors"] || []
24+
@user_errors = errors.map { |e| { :field => e["field"], :message => e["message"] } } if errors.any?
25+
@data = snake_case_keys(data.reject { |k, _| k == "userErrors" })
26+
end
27+
end
28+
end
29+
30+
private
31+
32+
def snake_case_keys(object)
33+
case object
34+
when Hash
35+
object.each_with_object({}) do |(key, value), result|
36+
result[Strings::Case.snakecase(key).to_sym] = snake_case_keys(value)
37+
end
38+
when Array
39+
object.map { |value| snake_case_keys(value) }
40+
else
41+
object
42+
end
43+
end
44+
end
45+
end
46+
end
47+
end
48+
end

spec/shopify_api/graphql/bulk_spec.rb

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@
122122
op = subject.result(ENV.fetch("BULK_FAILED_ID_USER_ERRORS"))
123123

124124
expect(op.results.size).to eq 1
125-
expect(op.results).to eq([
125+
expect(op.results[0]).to have_attributes(
126126
:line => 0,
127127
:data => {},
128+
:errors => [],
128129
:user_errors => [
129130
{
130131
:field => %w[query],
@@ -135,17 +136,19 @@
135136
:message =>"Variable $identifier of type ProductSetIdentifiers was provided invalid value"
136137
}
137138
]
138-
])
139+
)
139140
end
140141

141142
it "returns parsed errors" do
142143
op = subject.result(ENV.fetch("BULK_FAILED_ID_ERRORS"))
143144

144145
expect(op.results.size).to eq 1
145-
expect(op.results).to eq([
146+
expect(op.results[0]).to have_attributes(
146147
:line => 0,
147-
:errors => [:message => "OptionSetInput requires at least one of id, name"]
148-
])
148+
:data => nil,
149+
:errors => [{ :message => "OptionSetInput requires at least one of id, name" }],
150+
:user_errors => []
151+
)
149152
end
150153
end
151154

@@ -170,28 +173,30 @@
170173
op = subject.result(ENV.fetch("BULK_SUCCESS_ID"))
171174

172175
expect(op.results.size).to eq 2
173-
expect(op.results).to match([
174-
{
175-
:line => 0,
176-
:data => {
177-
:product => {
178-
:id => match(%r{\Agid://shopify/Product/\d+\z}),
179-
:handle => be_a(String),
180-
:tags => []
181-
}
176+
expect(op.results[0]).to have_attributes(
177+
:line => 0,
178+
:data => {
179+
:product => {
180+
:id => match(%r{\Agid://shopify/Product/\d+\z}),
181+
:handle => be_a(String),
182+
:tags => []
182183
}
183184
},
184-
{
185-
:line => 1,
186-
:data => {
187-
:product => {
188-
:id => match(%r{\Agid://shopify/Product/\d+\z}),
189-
:handle => be_a(String),
190-
:tags => []
191-
}
185+
:errors => [],
186+
:user_errors => []
187+
)
188+
expect(op.results[1]).to have_attributes(
189+
:line => 1,
190+
:data => {
191+
:product => {
192+
:id => match(%r{\Agid://shopify/Product/\d+\z}),
193+
:handle => be_a(String),
194+
:tags => []
192195
}
193-
}
194-
])
196+
},
197+
:errors => [],
198+
:user_errors => []
199+
)
195200
end
196201

197202
it "returns an Operation that does not contain the parsed data" do

0 commit comments

Comments
 (0)