graph_weaver 0.5.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +537 -0
- data/Gemfile.lock +19 -19
- data/README.md +74 -53
- data/docs/cassettes.md +29 -4
- data/docs/editors.md +3 -1
- data/docs/errors.md +75 -16
- data/docs/federation.md +206 -155
- data/docs/generated_modules.md +223 -166
- data/docs/getting_started.md +106 -82
- data/docs/logging.md +35 -5
- data/docs/scalars.md +119 -24
- data/docs/testing.md +196 -155
- data/docs/transports.md +47 -19
- data/docs/upgrading.md +243 -22
- data/graph_weaver.gemspec +16 -2
- data/lib/generators/graph_weaver/install_generator.rb +31 -16
- data/lib/graph_weaver/client.rb +52 -15
- data/lib/graph_weaver/codegen/aliases.rb +15 -8
- data/lib/graph_weaver/codegen/emit.rb +107 -42
- data/lib/graph_weaver/codegen/enum_type.rb +4 -3
- data/lib/graph_weaver/codegen/nodes.rb +42 -21
- data/lib/graph_weaver/codegen/scalar_type.rb +87 -83
- data/lib/graph_weaver/codegen/type_helpers.rb +2 -3
- data/lib/graph_weaver/codegen.rb +382 -105
- data/lib/graph_weaver/coerce.rb +113 -0
- data/lib/graph_weaver/errors.rb +57 -13
- data/lib/graph_weaver/federation.rb +10 -22
- data/lib/graph_weaver/hints.rb +76 -2
- data/lib/graph_weaver/in_process.rb +11 -8
- data/lib/graph_weaver/inflect.rb +2 -0
- data/lib/graph_weaver/input_struct.rb +115 -12
- data/lib/graph_weaver/internal/overrides.rb +101 -0
- data/lib/graph_weaver/internal/planner.rb +868 -0
- data/lib/graph_weaver/internal/schemas.rb +50 -0
- data/lib/graph_weaver/internal/selection.rb +127 -0
- data/lib/graph_weaver/{testing → internal}/subgraphs.rb +45 -43
- data/lib/graph_weaver/internal/values.rb +181 -0
- data/lib/graph_weaver/internal.rb +206 -0
- data/lib/graph_weaver/logging.rb +108 -20
- data/lib/graph_weaver/parsing.rb +6 -13
- data/lib/graph_weaver/query_module.rb +2 -0
- data/lib/graph_weaver/railtie.rb +113 -14
- data/lib/graph_weaver/representation.rb +30 -2
- data/lib/graph_weaver/response.rb +15 -0
- data/lib/graph_weaver/retry.rb +54 -22
- data/lib/graph_weaver/rspec.rb +63 -18
- data/lib/graph_weaver/schema_diff.rb +293 -0
- data/lib/graph_weaver/schema_loader.rb +126 -35
- data/lib/graph_weaver/tasks.rb +88 -36
- data/lib/graph_weaver/testing/cassette.rb +131 -78
- data/lib/graph_weaver/testing/coverage.rb +11 -15
- data/lib/graph_weaver/testing/failure.rb +14 -8
- data/lib/graph_weaver/testing/fake_client.rb +253 -60
- data/lib/graph_weaver/testing/fake_subgraph.rb +19 -8
- data/lib/graph_weaver/testing/router.rb +147 -840
- data/lib/graph_weaver/testing.rb +40 -83
- data/lib/graph_weaver/transport/faraday.rb +1 -1
- data/lib/graph_weaver/transport/http.rb +29 -12
- data/lib/graph_weaver/transport.rb +11 -34
- data/lib/graph_weaver/version.rb +1 -1
- data/lib/graph_weaver.rb +221 -118
- metadata +17 -13
- data/CLAUDE.md +0 -161
- data/DECISIONS.md +0 -309
- data/Makefile +0 -23
- data/NOTES.md +0 -182
- data/PLAN.md +0 -115
- data/REVIEW.md +0 -946
- data/lib/graph_weaver/schemas.rb +0 -46
- data/lib/graph_weaver/selection.rb +0 -120
- data/lib/graph_weaver/testing/values.rb +0 -98
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../internal"
|
|
5
|
+
|
|
6
|
+
module GraphWeaver
|
|
7
|
+
module Internal
|
|
8
|
+
# What a pin is: the key it may be written under, and how its value is
|
|
9
|
+
# read. Both doors need the answer — Testing.configure checks the
|
|
10
|
+
# suite-wide ones while the block that set them is still on the stack,
|
|
11
|
+
# and a fake built with its own checks them then.
|
|
12
|
+
module Overrides
|
|
13
|
+
class << self
|
|
14
|
+
# A pin key names something in the schema: a type ("Money",
|
|
15
|
+
# "Person"), a "Type.field" coordinate, or a bare field name
|
|
16
|
+
# matching that field on any type. Anything else is a typo that
|
|
17
|
+
# would silently fabricate random data instead of pinning a value.
|
|
18
|
+
def validate!(schema, overrides)
|
|
19
|
+
overrides.each do |key, value|
|
|
20
|
+
validate_arity!(key, value)
|
|
21
|
+
validate_key!(schema, key.to_s)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# A pin that's a proc is handed the seeded Random when it takes one
|
|
26
|
+
# and called bare when it doesn't, so a varying pin still reproduces
|
|
27
|
+
# under `rspec --seed`.
|
|
28
|
+
def resolve(value, rng)
|
|
29
|
+
return value unless value.is_a?(Proc)
|
|
30
|
+
|
|
31
|
+
value.arity.zero? ? value.call : value.call(rng)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# A proc taking anything else can't be called at fabrication time,
|
|
37
|
+
# and the ArgumentError it would raise there names no pin.
|
|
38
|
+
def validate_arity!(key, value)
|
|
39
|
+
return unless value.is_a?(Proc) && value.arity > 1
|
|
40
|
+
|
|
41
|
+
raise GraphWeaver::Error, "the pin for #{key.inspect} takes no arguments, or one — the " \
|
|
42
|
+
"seeded Random (-> (rng) { ... }); this one takes #{value.arity}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate_key!(schema, key)
|
|
46
|
+
type_name, field_name = key.split(".", 2)
|
|
47
|
+
# introspection fields (__typename) are real but absent from #fields
|
|
48
|
+
return if (field_name || type_name).start_with?("__")
|
|
49
|
+
|
|
50
|
+
if field_name.nil?
|
|
51
|
+
return if (type = schema.get_type(type_name)) && pinnable!(schema, key, type)
|
|
52
|
+
|
|
53
|
+
known = field_names(schema)
|
|
54
|
+
return if known.include?(type_name)
|
|
55
|
+
|
|
56
|
+
# a leading capital names a type, as it does wherever a pin is written
|
|
57
|
+
dictionary = type_name.match?(/\A[A-Z]/) ? schema.types.keys : known
|
|
58
|
+
bad!(key, "matches no type or field in this schema", dictionary, type_name)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
type = schema.get_type(type_name)
|
|
62
|
+
unless type.respond_to?(:fields)
|
|
63
|
+
bad!(key, "names no object type in this schema", schema.types.keys, type_name)
|
|
64
|
+
end
|
|
65
|
+
return if type.fields.key?(field_name)
|
|
66
|
+
|
|
67
|
+
bad!(key, "is not a field of #{type_name}", type.fields.keys, field_name)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# A type pin says what every value of that type is, and the fake only
|
|
71
|
+
# ever holds a concrete one: at an interface or union the walk has
|
|
72
|
+
# already picked a member, so a pin keyed on the abstract name would
|
|
73
|
+
# match nothing and leave the example green.
|
|
74
|
+
def pinnable!(schema, key, type)
|
|
75
|
+
return true if %w[SCALAR ENUM OBJECT].include?(type.kind.name)
|
|
76
|
+
|
|
77
|
+
advice = if type.kind.abstract?
|
|
78
|
+
members = schema.possible_types(type).map { |member| member.graphql_name.inspect }.sort
|
|
79
|
+
"pin the concrete type — #{members.join(", ")}"
|
|
80
|
+
else
|
|
81
|
+
"only output types are fabricated"
|
|
82
|
+
end
|
|
83
|
+
raise GraphWeaver::Error, "override key #{key.inspect} names #{type.kind.name.downcase.tr("_", " ")} " \
|
|
84
|
+
"#{type.graphql_name}, and a pin fabricates a scalar, enum or object: #{advice}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def bad!(key, problem, dictionary, term)
|
|
88
|
+
suggestion = Util.did_you_mean(dictionary, term)
|
|
89
|
+
hint = suggestion ? " — did you mean '#{suggestion}'?" : ""
|
|
90
|
+
raise GraphWeaver::Error, "override key #{key.inspect} #{problem}#{hint}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Every output field name in the schema — walked only when a bare key
|
|
94
|
+
# asks for it.
|
|
95
|
+
def field_names(schema)
|
|
96
|
+
schema.types.each_value.flat_map { |type| type.respond_to?(:fields) ? type.fields.keys : [] }.uniq
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|