Skip to content

Commit ded41f6

Browse files
authored
Merge pull request #19 from gmac/gmac/response_transformer_tests
Response transformer tests
2 parents d5a34d7 + 586c775 commit ded41f6

35 files changed

+1019
-116
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,22 @@ An App schema promotes an app-owned custom data namespace as base fields and typ
153153
client = ShopifyCustomDataGraphQL::Client.new(
154154
# ...
155155
base_namespaces: ["$app"],
156-
prefixed_namespaces: ["$app:*", "app--*", "custom", "other"],
156+
prefixed_namespaces: ["$app:*", "app--*", "custom"],
157157
app_context_id: 123,
158158
)
159159
```
160160

161161
Results in:
162162

163163
* **`custom.my_field`**`custom_myField`
164-
* **`other.my_field`**`other_myField`
165164
* **`app--123.my_field`**: → `myField`
166165
* **`app--123--other.my_field`**`other_myField`
167166
* **`app--456.my_field`**`app456_myField`
168167
* **`my_type`**`MyTypeShopMetaobject`
169168
* **`app--123--my_type`**`MyTypeMetaobject`
170169
* **`app--456--my_type`**`MyTypeApp456Metaobject`
171170

172-
Providing just `app_context_id` will automatically filter the schema down to just `$app` fields and types owned by the specified client.
171+
Providing just `app_context_id` will automatically filter the schema down to just `$app` fields and types owned by the specified app id.
173172

174173
### Combined namespaces
175174

example/Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ gem 'rack'
77
gem 'rackup'
88
gem 'graphql'
99
gem 'activesupport'
10+
gem 'rainbow'

example/server.rb

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'rackup'
55
require 'json'
66
require 'graphql'
7+
require 'rainbow'
78
require_relative '../lib/shopify_custom_data_graphql'
89

910
class App
@@ -31,30 +32,49 @@ def initialize
3132
@client.on_cache_read { |k| @mock_cache[k] }
3233
@client.on_cache_write { |k, v| @mock_cache[k] = v }
3334

34-
puts "Loading custom data schema..."
35+
puts Rainbow("Loading custom data schema...").cyan.bright
3536
@client.eager_load!
36-
puts "Done."
37+
puts Rainbow("Done.").cyan
3738
end
3839

3940
def call(env)
4041
req = Rack::Request.new(env)
4142
case req.path_info
4243
when /graphql/
4344
params = JSON.parse(req.body.read)
44-
result = @client.execute(
45-
query: params["query"],
46-
variables: params["variables"],
47-
operation_name: params["operationName"],
48-
)
45+
result = log_result do
46+
@client.execute(
47+
query: params["query"],
48+
variables: params["variables"],
49+
operation_name: params["operationName"],
50+
)
51+
end
4952

50-
[200, {"content-type" => "application/json"}, [JSON.generate(result)]]
53+
[200, {"content-type" => "application/json"}, [JSON.generate(result.to_h)]]
5154
when /refresh/
52-
reload_shop_schema
55+
@client.schema(reload_custom_schema: true)
5356
[200, {"content-type" => "text/html"}, ["Shop schema refreshed!"]]
5457
else
5558
[200, {"content-type" => "text/html"}, [@graphiql]]
5659
end
5760
end
61+
62+
def log_result
63+
timestamp = Time.current
64+
result = yield
65+
message = [Rainbow("[request #{timestamp.to_s}]").cyan.bright]
66+
stats = ["validate", "introspection", "transform_request", "proxy", "transform_response"].filter_map do |stat|
67+
time = result.tracer[stat]
68+
next unless time
69+
70+
"#{Rainbow(stat).magenta}: #{(time * 100).round / 100.to_f}ms"
71+
end
72+
73+
message << stats.join(", ")
74+
message << "\n#{result.query}" if result.tracer["transform_request"]
75+
puts message.join(" ")
76+
result
77+
end
5878
end
5979

6080
Rackup::Handler.default.run(App.new, :Port => 3000)

lib/shopify_custom_data_graphql/client.rb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,17 @@ def schema(reload_custom_schema: false, reload_admin_schema: false)
103103

104104
def execute(query: nil, variables: nil, operation_name: nil)
105105
tracer = Tracer.new
106-
result = tracer.span("execute") do
107-
prepare_query(query, operation_name, tracer) do |admin_query|
106+
tracer.span("execute") do
107+
perform_query(query, operation_name, tracer) do |admin_query|
108108
@admin.fetch(admin_query, variables: variables)
109109
end
110110
end
111-
112-
puts tracer.as_json
113-
result
114111
rescue ValidationError => e
115-
{ "errors" => e.errors }
112+
PreparedQuery::Result.new(
113+
query: query,
114+
tracer: tracer,
115+
result: { "errors" => e.errors },
116+
)
116117
end
117118

118119
def on_cache_read(&block)
@@ -127,7 +128,7 @@ def on_cache_write(&block)
127128

128129
private
129130

130-
def prepare_query(query_str, operation_name, tracer, &block)
131+
def perform_query(query_str, operation_name, tracer, &block)
131132
digest = @digest_class.hexdigest([query_str, operation_name, VERSION].join(" "))
132133
if @lru && (hot_query = @lru.get(digest))
133134
return hot_query.perform(tracer, source_query: query_str, &block)
@@ -147,9 +148,12 @@ def prepare_query(query_str, operation_name, tracer, &block)
147148
end
148149
raise ValidationError.new(errors: errors.map(&:to_h)) if errors.any?
149150

150-
return query.result.to_h if introspection_query?(query)
151+
if introspection_query?(query)
152+
result = tracer.span("introspection") { query.result.to_h }
153+
return PreparedQuery::Result.new(query: query_str, tracer: tracer, result: result)
154+
end
151155

152-
prepared_query = tracer.span("request_transform") do
156+
prepared_query = tracer.span("transform_request") do
153157
RequestTransformer.new(query).perform.to_prepared_query
154158
end
155159
json = prepared_query.to_json

lib/shopify_custom_data_graphql/prepared_query.rb

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ module ShopifyCustomDataGraphQL
44
class PreparedQuery
55
DEFAULT_TRACER = Tracer.new
66

7+
class Result
8+
attr_reader :query, :tracer, :result
9+
10+
def initialize(query:, tracer:, result:)
11+
@query = query
12+
@tracer = tracer
13+
@result = result
14+
end
15+
16+
def to_h
17+
@result
18+
end
19+
end
20+
721
attr_reader :query, :transforms
822

923
def initialize(params)
@@ -15,10 +29,6 @@ def initialize(params)
1529
end
1630
end
1731

18-
def has_transforms?
19-
@transforms.any?
20-
end
21-
2232
def as_json
2333
{
2434
"query" => @query,
@@ -31,16 +41,18 @@ def to_json
3141
end
3242

3343
def perform(tracer = DEFAULT_TRACER, source_query: nil)
34-
# pass through source query when it requires no transforms
35-
# stops queries without transformations from taking up cache space
36-
return yield(source_query) if source_query && !has_transforms?
37-
38-
response = tracer.span("proxy") do
39-
yield(@query)
40-
end
41-
tracer.span("transform_response") do
42-
ResponseTransformer.new(@transforms).perform(response)
44+
query = source_query && @transforms.none? ? source_query : @query
45+
raw_result = tracer.span("proxy") { yield(query) }
46+
47+
result = if @transforms.any?
48+
tracer.span("transform_response") do
49+
ResponseTransformer.new(@transforms).perform(raw_result)
50+
end
51+
else
52+
raw_result
4353
end
54+
55+
Result.new(query: query, tracer: tracer, result: result)
4456
end
4557
end
4658
end

lib/shopify_custom_data_graphql/request_transformer.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def initialize(query, metafield_ns = "custom")
4242
@query = query
4343
@schema = query.schema
4444
@app_context = directive_kwargs(@schema.schema_directives, "app")&.dig(:id)
45-
@owner_types = @schema.possible_types(@schema.get_type("HasMetafields")).to_set
45+
@owner_types = @schema.possible_types(@query.get_type("HasMetafields")).to_set
4646
@root_ext_name = MetafieldTypeResolver.extensions_typename(@schema.query.graphql_name)
4747
@transform_map = TransformationMap.new(@app_context)
4848
@metafield_ns = metafield_ns
@@ -71,13 +71,13 @@ def transform_scope(parent_type, input_selections, scope_type: NATIVE_SCOPE, sco
7171
if scope_type == NATIVE_SCOPE && node.name == "extensions" && (parent_type == @schema.query || @owner_types.include?(parent_type))
7272
@transform_map.apply_field_transform(FieldTransform.new(NAMESPACE_TRANSFORM))
7373
with_namespace_anchor_field(node) do
74-
next_type = parent_type.get_field(node.name).type.unwrap
74+
next_type = @query.get_field(parent_type, node.name).type.unwrap
7575
transform_scope(next_type, node.selections, scope_type: EXTENSIONS_SCOPE, scope_ns: node.alias || node.name)
7676
end
7777
elsif scope_type == METAOBJECT_SCOPE && node.name == "system"
7878
@transform_map.apply_field_transform(FieldTransform.new(NAMESPACE_TRANSFORM))
7979
with_namespace_anchor_field(node) do
80-
next_type = parent_type.get_field(node.name).type.unwrap
80+
next_type = @query.get_field(parent_type, node.name).type.unwrap
8181
transform_scope(next_type, node.selections, scope_ns: node.alias || node.name)
8282
end
8383
elsif scope_type == EXTENSIONS_SCOPE && parent_type.graphql_name == @root_ext_name
@@ -89,15 +89,15 @@ def transform_scope(parent_type, input_selections, scope_type: NATIVE_SCOPE, sco
8989
node = node.merge(alias: "#{RESERVED_PREFIX}#{scope_ns}_#{node.alias || node.name}")
9090
end
9191
if node.selections&.any?
92-
next_type = parent_type.get_field(node.name).type.unwrap
92+
next_type = @query.get_field(parent_type, node.name).type.unwrap
9393
node = node.merge(selections: transform_scope(next_type, node.selections))
9494
end
9595
node
9696
end
9797
end
9898

9999
when GraphQL::Language::Nodes::InlineFragment
100-
fragment_type = node.type.nil? ? parent_type : @schema.get_type(node.type.name)
100+
fragment_type = node.type.nil? ? parent_type : @query.get_type(node.type.name)
101101
with_typed_condition(parent_type, fragment_type, scope_type) do
102102
if MetafieldTypeResolver.extensions_type?(fragment_type.graphql_name)
103103
transform_scope(fragment_type, node.selections, scope_type: EXTENSIONS_SCOPE, scope_ns: scope_ns)
@@ -116,7 +116,7 @@ def transform_scope(parent_type, input_selections, scope_type: NATIVE_SCOPE, sco
116116

117117
when GraphQL::Language::Nodes::FragmentSpread
118118
fragment_def = @query.fragments[node.name]
119-
fragment_type = @schema.get_type(fragment_def.type.name)
119+
fragment_type = @query.get_type(fragment_def.type.name)
120120
with_typed_condition(parent_type, fragment_type, scope_type) do
121121
unless @new_fragments[node.name]
122122
fragment_type_name = fragment_type.graphql_name
@@ -175,7 +175,7 @@ def with_typed_condition(parent_type, fragment_type, scope_type)
175175

176176
# connections must map transformations through possible `edges -> node` and `nodes` pathways
177177
def build_connection_selections(conn_type, conn_node)
178-
conn_node_type = conn_type.get_field("nodes").type.unwrap
178+
conn_node_type = @query.get_field(conn_type, "nodes").type.unwrap
179179
conn_node.selections.map do |node|
180180
@transform_map.field_breadcrumb(node) do
181181
case node.name
@@ -186,7 +186,7 @@ def build_connection_selections(conn_type, conn_node)
186186
when "node"
187187
n.merge(selections: yield(conn_node_type, n.selections))
188188
when GQL_TYPENAME
189-
edge_type = conn_type.get_field("edges").type.unwrap
189+
edge_type = @query.get_field(conn_type, "edges").type.unwrap
190190
@transform_map.apply_field_transform(FieldTransform.new(STATIC_TYPENAME_TRANSFORM, value: edge_type.graphql_name))
191191
n
192192
else
@@ -210,10 +210,10 @@ def build_connection_selections(conn_type, conn_node)
210210
def build_metaobject_query(parent_type, node, scope_ns: nil)
211211
return build_typename(parent_type, node, scope_type: EXTENSIONS_SCOPE, scope_ns: scope_ns) if node.name == GQL_TYPENAME
212212

213-
field_type = parent_type.get_field(node.name).type.unwrap
213+
field_type = @query.get_field(parent_type, node.name).type.unwrap
214214
return node unless MetafieldTypeResolver.connection_type?(field_type.graphql_name)
215215

216-
node_type = field_type.get_field("nodes").type.unwrap
216+
node_type = @query.get_field(field_type, "nodes").type.unwrap
217217
metaobject_type = directive_kwargs(node_type.directives, "metaobject")&.dig(:type)
218218
return node unless metaobject_type
219219

@@ -232,7 +232,7 @@ def build_metaobject_query(parent_type, node, scope_ns: nil)
232232
def build_metafield(parent_type, node, scope_type:, scope_ns: nil)
233233
return build_typename(parent_type, node, scope_type: scope_type, scope_ns: scope_ns) if node.name == GQL_TYPENAME
234234

235-
field = parent_type.get_field(node.name)
235+
field = @query.get_field(parent_type, node.name)
236236
metafield_attrs = directive_kwargs(field.directives, "metafield")
237237
return node unless metafield_attrs
238238

shopify_custom_data_graphql.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ Gem::Specification.new do |spec|
3232
spec.add_development_dependency "bundler", "~> 2.0"
3333
spec.add_development_dependency "rake", "~> 12.0"
3434
spec.add_development_dependency "minitest", "~> 5.12"
35+
spec.add_development_dependency "graphql-response_validator", "~> 0.0.2"
3536
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"data": {
3+
"product": {
4+
"extensions": "Product",
5+
"___extensions_rating": {
6+
"jsonValue": {
7+
"value": 4.5,
8+
"scale_max": 5,
9+
"scale_min": 0
10+
}
11+
}
12+
}
13+
}
14+
}

test/fixtures/casettes/errors_with_list_path.json renamed to test/fixtures/responses/errors_with_list_path.json

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,5 @@
2121
}
2222
}
2323
],
24-
"data": {
25-
"products": {
26-
"nodes": [
27-
{
28-
"extensions": {
29-
"widget": {
30-
"system": {
31-
"createdByStaff": null
32-
}
33-
}
34-
}
35-
}
36-
]
37-
}
38-
}
24+
"data": null
3925
}

test/fixtures/casettes/errors_with_object_path.json renamed to test/fixtures/responses/errors_with_object_path.json

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,5 @@
1919
}
2020
}
2121
],
22-
"data": {
23-
"product": {
24-
"extensions": {
25-
"widget": {
26-
"system": {
27-
"createdByStaff": null
28-
}
29-
}
30-
}
31-
}
32-
}
22+
"data": null
3323
}

0 commit comments

Comments
 (0)