graph_weaver 0.4.6 → 0.5.1
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 +1442 -0
- data/Gemfile.lock +23 -23
- data/README.md +115 -96
- data/docs/cassettes.md +93 -46
- data/docs/editors.md +82 -0
- data/docs/errors.md +34 -30
- data/docs/federation.md +521 -48
- data/docs/generated_modules.md +352 -137
- data/docs/getting_started.md +237 -67
- data/docs/logging.md +35 -6
- data/docs/real_world.md +21 -15
- data/docs/scalars.md +49 -154
- data/docs/testing.md +300 -52
- data/docs/transports.md +129 -30
- data/docs/upgrading.md +134 -0
- data/graph_weaver.gemspec +19 -3
- data/lib/generators/graph_weaver/install_generator.rb +259 -0
- data/lib/graph_weaver/client.rb +118 -111
- data/lib/graph_weaver/codegen/aliases.rb +223 -0
- data/lib/graph_weaver/codegen/emit.rb +283 -261
- data/lib/graph_weaver/codegen/enum_type.rb +25 -124
- data/lib/graph_weaver/codegen/nodes.rb +72 -13
- data/lib/graph_weaver/codegen/scalar_type.rb +69 -66
- data/lib/graph_weaver/codegen/type_helpers.rb +140 -0
- data/lib/graph_weaver/codegen.rb +672 -336
- data/lib/graph_weaver/errors.rb +154 -16
- data/lib/graph_weaver/federation.rb +259 -0
- data/lib/graph_weaver/hints.rb +9 -1
- data/lib/graph_weaver/in_process.rb +90 -0
- data/lib/graph_weaver/input_struct.rb +14 -2
- data/lib/graph_weaver/logging.rb +29 -0
- data/lib/graph_weaver/parsing.rb +59 -0
- data/lib/graph_weaver/query_module.rb +55 -0
- data/lib/graph_weaver/railtie.rb +23 -1
- data/lib/graph_weaver/representation.rb +74 -0
- data/lib/graph_weaver/response.rb +7 -0
- data/lib/graph_weaver/retry.rb +29 -8
- data/lib/graph_weaver/rspec.rb +220 -16
- data/lib/graph_weaver/schema_loader.rb +819 -60
- data/lib/graph_weaver/schemas.rb +48 -0
- data/lib/graph_weaver/selection.rb +43 -8
- data/lib/graph_weaver/tasks.rb +220 -22
- data/lib/graph_weaver/testing/cassette.rb +249 -81
- data/lib/graph_weaver/testing/coverage.rb +160 -0
- data/lib/graph_weaver/testing/failure.rb +14 -25
- data/lib/graph_weaver/testing/fake_client.rb +182 -22
- data/lib/graph_weaver/testing/fake_subgraph.rb +85 -0
- data/lib/graph_weaver/testing/router.rb +1452 -0
- data/lib/graph_weaver/testing/subgraphs.rb +134 -0
- data/lib/graph_weaver/testing.rb +209 -13
- data/lib/graph_weaver/transport/faraday.rb +28 -10
- data/lib/graph_weaver/transport/http.rb +99 -36
- data/lib/graph_weaver/transport.rb +67 -14
- data/lib/graph_weaver/version.rb +1 -1
- data/lib/graph_weaver.rb +416 -172
- metadata +25 -9
- data/CLAUDE.md +0 -69
- data/Makefile +0 -23
- data/NOTES.md +0 -182
- data/PLAN.md +0 -144
data/docs/upgrading.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Upgrading to 0.5.0
|
|
2
|
+
|
|
3
|
+
0.5.0 is one large breaking release. Almost all of it is caught mechanically,
|
|
4
|
+
in this order:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
# 1. rename the path settings first — generate won't load without them
|
|
8
|
+
# (queries_path -> queries_paths, generated_path -> generated_paths,
|
|
9
|
+
# fragments_path -> fragments_paths; see "One plurality rule" below)
|
|
10
|
+
|
|
11
|
+
bundle exec tapioca gem graph_weaver # 2. regenerate the RBI
|
|
12
|
+
rake graph_weaver:generate # 3. the emitted call shape changed
|
|
13
|
+
srb tc # 4. every call site that moved is an error
|
|
14
|
+
rake graph_weaver:verify # 5. fails until the tree is regenerated
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Step 2 is not optional.** Against the 0.4.6 RBI, `srb tc` reports errors
|
|
18
|
+
pointing into your `generated/` directory — `QueryModule`, `client_for`,
|
|
19
|
+
`check_envelope!` — which read as though codegen emitted broken Ruby. It
|
|
20
|
+
didn't; sorbet is checking new generated code against the old gem's types.
|
|
21
|
+
Regenerate the RBI and what remains is only your own call sites.
|
|
22
|
+
|
|
23
|
+
Generated code is `# typed: strict`, so step 4 finds those for you. The rest of
|
|
24
|
+
this page is what a typechecker can't see.
|
|
25
|
+
|
|
26
|
+
## `execute` means one thing now
|
|
27
|
+
|
|
28
|
+
Every client answers the same call — `execute(query, variables:, operation_name:)`,
|
|
29
|
+
returning the raw response hash. `Client` used to spell something else under
|
|
30
|
+
that name, which is why `Retry.new(client)` and `Sequence.new(client, fake)`
|
|
31
|
+
raised `ArgumentError`. They work now.
|
|
32
|
+
|
|
33
|
+
The one-shot sugar moved to `run`:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
client.execute!("query { … }", id: "1") # before
|
|
37
|
+
client.run!("query { … }", id: "1") # after (and #run for the envelope)
|
|
38
|
+
|
|
39
|
+
GraphWeaver.execute(source, query, **vars) # before
|
|
40
|
+
GraphWeaver.run(source, query, **vars) # after
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**This one is worth grepping for.** `Client#execute` still exists, so a stale
|
|
44
|
+
call fails at runtime rather than at typecheck — as do `GraphWeaver.execute`
|
|
45
|
+
and `GraphWeaver.reset_scalars!`, which are simply gone and will not be flagged
|
|
46
|
+
until the RBI is regenerated (step 2): `rg '\.execute!?\(' --type ruby`
|
|
47
|
+
and check each hit is passing `variables:` rather than loose kwargs.
|
|
48
|
+
|
|
49
|
+
A generated module takes its per-call client as a **keyword**:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
PersonQuery.execute(some_client, id: "1") # before
|
|
53
|
+
PersonQuery.execute(client: some_client, id: "1") # after
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`GraphWeaver.resolve_transport` is gone; nothing needs unwrapping any more.
|
|
57
|
+
|
|
58
|
+
## Path settings are lists
|
|
59
|
+
|
|
60
|
+
`queries_paths`, `generated_paths`, `fragments_paths` — every entry is read.
|
|
61
|
+
Assigning a String still works, so the change is the name:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
GraphWeaver.queries_path = "app/graphql/queries" # before
|
|
65
|
+
GraphWeaver.queries_paths = "app/graphql/queries" # after
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`schema_path` stays singular: one run reads one schema.
|
|
69
|
+
|
|
70
|
+
## One reset
|
|
71
|
+
|
|
72
|
+
`GraphWeaver.reset_registrations!` is the clean slate between tests. The four
|
|
73
|
+
narrow ones moved to where they live:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
GraphWeaver.reset_scalars! # before
|
|
77
|
+
GraphWeaver::Codegen.reset_scalars! # after (also reset_enums!, clear_scalars!,
|
|
78
|
+
# reset_type_helpers!)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Smaller renames
|
|
82
|
+
|
|
83
|
+
| before | after |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `Testing.config.auto_fake = true` | `Testing.config.default_mode = :fake` |
|
|
86
|
+
| `register_scalar(…, coerce: :to_s)` | `coerce: true`, or a `cast:`/`serialize:` pair |
|
|
87
|
+
| a mutation's `…Query` module | `…Mutation` |
|
|
88
|
+
| `graphql: :none` (rspec tag) | `graphql: false` |
|
|
89
|
+
|
|
90
|
+
**The shared types module was three, and is now one.** `GraphQLInputs`,
|
|
91
|
+
`GraphQLEnums` and `GraphQLUnions` are all `GraphQLTypes`, and the files move
|
|
92
|
+
with them — `generated/inputs/` becomes `generated/types/`. The three settings
|
|
93
|
+
that named them (`inputs_module=`, `enums_module=`, `unions_module=`) are one
|
|
94
|
+
`types_module=`. Regenerating writes the new tree; delete the old directory,
|
|
95
|
+
which pruning leaves behind empty.
|
|
96
|
+
|
|
97
|
+
If your specs run one schema class in-process while your client points at a
|
|
98
|
+
different API, name it — per example, since a federated suite runs more than
|
|
99
|
+
one:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
graphql_in_process(MySchema) # in the example
|
|
103
|
+
GraphWeaver::Testing.config.schema = MySchema # or once, for the whole suite
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Registering from Rails
|
|
107
|
+
|
|
108
|
+
A registration naming one of your own constants belongs in a `to_prepare` block
|
|
109
|
+
— the same place the in-process client goes, and for the same reason:
|
|
110
|
+
autoloading is set up after `config/initializers` run.
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
Rails.application.config.to_prepare do
|
|
114
|
+
GraphWeaver.register_enum("Species", PetKind, fallback: PetKind::Unknown)
|
|
115
|
+
GraphWeaver.extend_type("Pet", PetHelpers)
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Generation depends on `:environment`, which runs `to_prepare` too, so the
|
|
120
|
+
registration is in place before it emits.
|
|
121
|
+
|
|
122
|
+
## If you use the federation router
|
|
123
|
+
|
|
124
|
+
Detection only sees *loaded* schema classes, and Rails does not eager load for
|
|
125
|
+
rake or in the default test environment. Both are one line:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
config.eager_load = true # config/environments/test.rb
|
|
129
|
+
config.rake_eager_load = true # config/application.rb
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Without them the `federation:*` tasks silently see nothing — and
|
|
133
|
+
`federation:diff` now **fails** rather than reporting a green "matches" over
|
|
134
|
+
zero subgraphs.
|
data/graph_weaver.gemspec
CHANGED
|
@@ -2,16 +2,32 @@ require_relative "lib/graph_weaver/version"
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.authors = ["Daniel Pepper"]
|
|
5
|
-
|
|
5
|
+
# the README tagline, verbatim — the two pitches drifted apart once already,
|
|
6
|
+
# so spec/gemspec_spec.rb pins them together
|
|
7
|
+
s.description = "Your .graphql files, compiled into Sorbet types — and the fakes to test them."
|
|
6
8
|
# ".yardopts" explicitly: `git ls-files *` skips dotfiles, and
|
|
7
9
|
# rubydoc.info needs it shipped to render docstrings as markdown
|
|
8
|
-
|
|
10
|
+
# CLAUDE.md/PLAN.md/REVIEW.md/NOTES.md/DECISIONS.md are written for whoever
|
|
11
|
+
# works on the gem, not whoever installs it — and REVIEW.md carries examples
|
|
12
|
+
# from before the API it describes was rewritten
|
|
13
|
+
s.files = `git ls-files * ':!:spec' ':!:sorbet' ':!:bin' ':!:examples' \
|
|
14
|
+
':!:CLAUDE.md' ':!:PLAN.md' ':!:REVIEW.md' ':!:NOTES.md' \
|
|
15
|
+
':!:DECISIONS.md' ':!:Makefile' ':!:design'`.split("\n") + [".yardopts"]
|
|
9
16
|
s.homepage = "https://github.com/dpep/graph_weaver"
|
|
10
17
|
s.license = "MIT"
|
|
11
18
|
s.name = "graph_weaver"
|
|
12
|
-
|
|
19
|
+
# rubygems.org shows summary as the headline, description below it
|
|
20
|
+
s.summary = "A typed GraphQL client for Ruby"
|
|
13
21
|
s.version = GraphWeaver::VERSION
|
|
14
22
|
|
|
23
|
+
s.metadata = {
|
|
24
|
+
"bug_tracker_uri" => "#{s.homepage}/issues",
|
|
25
|
+
"changelog_uri" => "#{s.homepage}/blob/main/CHANGELOG.md",
|
|
26
|
+
"documentation_uri" => "#{s.homepage}/tree/main/docs",
|
|
27
|
+
"rubygems_mfa_required" => "true",
|
|
28
|
+
"source_code_uri" => s.homepage,
|
|
29
|
+
}
|
|
30
|
+
|
|
15
31
|
s.required_ruby_version = ">= 3.3"
|
|
16
32
|
|
|
17
33
|
# 2.6.7 fills defaulted directive arguments when building from SDL
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# typed: ignore — Rails::Generators DSL, only loaded by `rails g`
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "graph_weaver"
|
|
5
|
+
|
|
6
|
+
# rails g graph_weaver:install https://api.example.com/graphql
|
|
7
|
+
# rails g graph_weaver:install MyApp::Schema
|
|
8
|
+
# rails g graph_weaver:install db/schema.graphql
|
|
9
|
+
#
|
|
10
|
+
# Scaffolds the conventional layout — initializer, query/generated
|
|
11
|
+
# directories, editor config — and bootstraps the schema dump, so the
|
|
12
|
+
# setup in docs/getting_started.md is one command.
|
|
13
|
+
#
|
|
14
|
+
# The argument is what you'd pass to GraphWeaver.new, and the same three
|
|
15
|
+
# source forms are accepted; the initializer it writes reflects the one
|
|
16
|
+
# you chose. The source arrives on the command line rather than being read
|
|
17
|
+
# from config: at install time the initializer doesn't exist yet.
|
|
18
|
+
module GraphWeaver
|
|
19
|
+
module Generators
|
|
20
|
+
class InstallGenerator < Rails::Generators::Base
|
|
21
|
+
desc <<~TEXT
|
|
22
|
+
Wire up GraphWeaver: initializer, app/graphql layout, editor config, schema dump.
|
|
23
|
+
|
|
24
|
+
SOURCE is what you'd pass to GraphWeaver.new:
|
|
25
|
+
|
|
26
|
+
rails g graph_weaver:install https://api.example.com/graphql # an endpoint
|
|
27
|
+
rails g graph_weaver:install MyApp::Schema # a graphql-ruby schema, in-process
|
|
28
|
+
rails g graph_weaver:install db/schema.graphql # a schema dump you already have
|
|
29
|
+
TEXT
|
|
30
|
+
|
|
31
|
+
argument :source, type: :string, banner: "SOURCE",
|
|
32
|
+
desc: "what you'd pass to GraphWeaver.new: an endpoint url, a graphql-ruby schema class, or a schema dump path"
|
|
33
|
+
|
|
34
|
+
# the default is SchemaLoader's, not one restated here — an --auth the
|
|
35
|
+
# generator omits from the dump is one the schema tasks then can't find
|
|
36
|
+
class_option :auth, type: :string,
|
|
37
|
+
desc: "name of the ENV var holding the auth token (url only) — " \
|
|
38
|
+
"default #{GraphWeaver::SchemaLoader::DEFAULT_AUTH_ENV}"
|
|
39
|
+
class_option :schema, type: :boolean, default: true,
|
|
40
|
+
desc: "write the schema dump codegen reads"
|
|
41
|
+
|
|
42
|
+
# Before anything is written: a mistyped source or a flag that doesn't
|
|
43
|
+
# apply to it is a mistake in the command just typed, so say so there
|
|
44
|
+
# rather than at boot, three files later.
|
|
45
|
+
def check_source
|
|
46
|
+
if options[:auth] && form != :url
|
|
47
|
+
raise Thor::Error, "--auth applies to a url — #{source} is a #{form == :schema_class ? "schema class" : "schema dump"}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
schema_class if form == :schema_class
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Every write goes through create_file, so a re-run prompts with a
|
|
54
|
+
# diff rather than overwriting an initializer you've edited.
|
|
55
|
+
def create_initializer
|
|
56
|
+
create_file "config/initializers/graph_weaver.rb", initializer
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def create_layout
|
|
60
|
+
create_file File.join(GraphWeaver.queries_paths.first, ".keep"), ""
|
|
61
|
+
create_file File.join(GraphWeaver.generated_paths.first, ".keep"), ""
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# editor autocomplete + validation for .graphql files (docs/editors.md)
|
|
65
|
+
def create_editor_config
|
|
66
|
+
create_file "graphql.config.yml", editor_config
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# A url is introspected and a schema class dumped; a dump the app
|
|
70
|
+
# already has is left where it is (schema_path points at it instead).
|
|
71
|
+
def fetch_schema
|
|
72
|
+
return unless options[:schema] && form != :path
|
|
73
|
+
|
|
74
|
+
if form == :url
|
|
75
|
+
# pass the var name, not just the token — it lands in the dump's
|
|
76
|
+
# provenance so schema:refresh/:diff read the same one the
|
|
77
|
+
# initializer does, instead of defaulting to GRAPHWEAVER_AUTH
|
|
78
|
+
GraphWeaver::SchemaLoader.refresh!(url: source, auth_env: auth_var)
|
|
79
|
+
else
|
|
80
|
+
# a schema class is its own introspection source; ttl: 0 so an
|
|
81
|
+
# existing dump never counts as fresh
|
|
82
|
+
GraphWeaver::SchemaLoader.introspect(schema_class, cache: schema_path, ttl: 0)
|
|
83
|
+
end
|
|
84
|
+
say_status :introspect, "#{schema_path} from #{source}"
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
# the files above are the valuable part — don't lose them to a bad
|
|
87
|
+
# token or an unreachable host
|
|
88
|
+
say_status :failed, "#{e.message} — retry with `#{refresh_command}`", :red
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def next_steps
|
|
92
|
+
say <<~TEXT
|
|
93
|
+
|
|
94
|
+
Write a query in #{GraphWeaver.queries_paths.first}, then:
|
|
95
|
+
|
|
96
|
+
rake graph_weaver:generate
|
|
97
|
+
|
|
98
|
+
Docs: https://github.com/dpep/graph_weaver/blob/main/docs/getting_started.md
|
|
99
|
+
TEXT
|
|
100
|
+
|
|
101
|
+
say federated_steps if subgraphs
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
# This install run is the one moment the user is guaranteed to be
|
|
107
|
+
# reading, and a composed supergraph changes what the next steps are:
|
|
108
|
+
# the test client is the interesting one, and there's a CI gate to add.
|
|
109
|
+
def federated_steps
|
|
110
|
+
<<~TEXT
|
|
111
|
+
|
|
112
|
+
#{source} is a composed supergraph (#{subgraphs.size} subgraphs: #{subgraphs.join(", ")}), so:
|
|
113
|
+
|
|
114
|
+
rake graph_weaver:federation:diff # CI gate: a subgraph changed, nobody recomposed
|
|
115
|
+
rake graph_weaver:federation:subgraphs # which schema here serves which subgraph
|
|
116
|
+
|
|
117
|
+
and specs run against your real resolvers across all of them, in-process:
|
|
118
|
+
|
|
119
|
+
describe "checkout", graphql: :router do ... end # require "graph_weaver/rspec"
|
|
120
|
+
|
|
121
|
+
Docs: https://github.com/dpep/graph_weaver/blob/main/docs/federation.md
|
|
122
|
+
TEXT
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The subgraph names this source composes, or nil when it isn't a
|
|
126
|
+
# composed supergraph. Read off the routing table rather than guessed —
|
|
127
|
+
# the same reader Testing::Router and federation:diff use.
|
|
128
|
+
def subgraphs
|
|
129
|
+
return @subgraphs if defined?(@subgraphs)
|
|
130
|
+
|
|
131
|
+
@subgraphs =
|
|
132
|
+
begin
|
|
133
|
+
(GraphWeaver::SchemaLoader.routing_table(source).subgraphs if form == :path)
|
|
134
|
+
rescue StandardError
|
|
135
|
+
# not a supergraph, or not readable — nothing to say either way
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Which of GraphWeaver.new's source forms this is. Neither test is its
|
|
141
|
+
# own — a url is whatever the client calls one, a constant path whatever
|
|
142
|
+
# codegen will spell — so the generator can't disagree with either about
|
|
143
|
+
# what it just wrote an initializer for. Anything that is neither is
|
|
144
|
+
# taken as a path to a dump.
|
|
145
|
+
def form
|
|
146
|
+
@form ||=
|
|
147
|
+
if source.match?(GraphWeaver::Client::URL)
|
|
148
|
+
:url
|
|
149
|
+
elsif source.match?(GraphWeaver::Codegen::CONSTANT_NAME)
|
|
150
|
+
:schema_class
|
|
151
|
+
else
|
|
152
|
+
:path
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# The named schema class, resolved now: `rails g` boots the app, so a
|
|
157
|
+
# typo is catchable here rather than as a NameError at the next boot.
|
|
158
|
+
def schema_class
|
|
159
|
+
@schema_class ||= begin
|
|
160
|
+
klass = Object.const_get(source)
|
|
161
|
+
unless klass.respond_to?(:execute)
|
|
162
|
+
raise Thor::Error, "#{source} isn't a graphql-ruby schema (no .execute) — pass the class that inherits GraphQL::Schema"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
klass
|
|
166
|
+
rescue NameError
|
|
167
|
+
raise Thor::Error, "uninitialized constant #{source} — pass your graphql-ruby schema class " \
|
|
168
|
+
"(rails g graphql:install writes app/graphql/<app>_schema.rb), an endpoint url, or a path to a schema dump"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# A dump the app already has stays where it is; every other form
|
|
173
|
+
# writes the conventional one.
|
|
174
|
+
def schema_path = (form == :path) ? source : GraphWeaver.schema_path
|
|
175
|
+
|
|
176
|
+
def auth_var = options[:auth] || GraphWeaver::SchemaLoader::DEFAULT_AUTH_ENV
|
|
177
|
+
|
|
178
|
+
def refresh_command
|
|
179
|
+
(form == :url) ? "rake graph_weaver:schema:refresh" : "rails g graph_weaver:install #{source}"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def initializer
|
|
183
|
+
<<~RUBY
|
|
184
|
+
# frozen_string_literal: true
|
|
185
|
+
|
|
186
|
+
#{client_setup}
|
|
187
|
+
# Custom scalars, enums and type mixins go here — `rake graph_weaver:generate`
|
|
188
|
+
# bakes them into the generated source, so they must be registered first:
|
|
189
|
+
#
|
|
190
|
+
# GraphWeaver.register_scalar("DateTime", Time, serialize: :iso8601, requires: "time")
|
|
191
|
+
# GraphWeaver.extend_type("Person", Greetable)
|
|
192
|
+
RUBY
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# The load-bearing lines, per source form: what generated modules
|
|
196
|
+
# resolve to at execute time, and where codegen reads the schema.
|
|
197
|
+
def client_setup
|
|
198
|
+
case form
|
|
199
|
+
when :url
|
|
200
|
+
<<~RUBY
|
|
201
|
+
GraphWeaver.client = GraphWeaver.new(
|
|
202
|
+
"#{source}",
|
|
203
|
+
auth: ENV["#{auth_var}"],
|
|
204
|
+
cache: true, # reuse the committed dump; delete it to re-introspect
|
|
205
|
+
)
|
|
206
|
+
RUBY
|
|
207
|
+
when :schema_class
|
|
208
|
+
# to_prepare, not a bare assignment: the schema class is autoloaded,
|
|
209
|
+
# so it isn't resolvable this early, and a dev reload replaces it
|
|
210
|
+
# with a new class object the client would otherwise still hold.
|
|
211
|
+
<<~RUBY
|
|
212
|
+
Rails.application.config.to_prepare do
|
|
213
|
+
# queries run in-process against the app's own schema — no socket
|
|
214
|
+
GraphWeaver.client = GraphWeaver.new(#{source})
|
|
215
|
+
end
|
|
216
|
+
RUBY
|
|
217
|
+
when :path
|
|
218
|
+
if subgraphs
|
|
219
|
+
<<~RUBY
|
|
220
|
+
GraphWeaver.schema_path = "#{source}"
|
|
221
|
+
|
|
222
|
+
# A composed supergraph: your queries are generated against the whole
|
|
223
|
+
# graph, and a router serves it. Point the app default at the gateway:
|
|
224
|
+
#
|
|
225
|
+
# GraphWeaver.client = GraphWeaver.new("https://gateway.example.com/graphql")
|
|
226
|
+
#
|
|
227
|
+
# Specs don't need one — `graphql: :router` plans against this
|
|
228
|
+
# supergraph and runs your own subgraph resolvers in-process
|
|
229
|
+
# (docs/federation.md).
|
|
230
|
+
RUBY
|
|
231
|
+
else
|
|
232
|
+
<<~RUBY
|
|
233
|
+
GraphWeaver.schema_path = "#{source}"
|
|
234
|
+
|
|
235
|
+
# A dump is type information only — it has no resolvers, so it can't
|
|
236
|
+
# execute. Point the app default at whatever serves this API:
|
|
237
|
+
#
|
|
238
|
+
# GraphWeaver.client = GraphWeaver.new("https://api.example.com/graphql")
|
|
239
|
+
RUBY
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# fragments are in documents: too — without them an editor reports
|
|
245
|
+
# `Unknown fragment` on any query that spreads a shared one. The glob is
|
|
246
|
+
# codegen's, so the editor validates exactly the files codegen reads.
|
|
247
|
+
def editor_config
|
|
248
|
+
<<~YAML
|
|
249
|
+
# Autocomplete and validation for .graphql files in VS Code / RubyMine.
|
|
250
|
+
# https://github.com/dpep/graph_weaver/blob/main/docs/editors.md
|
|
251
|
+
schema: #{schema_path}
|
|
252
|
+
documents:
|
|
253
|
+
- #{File.join(GraphWeaver.queries_paths.first, GraphWeaver::Codegen::DOCUMENT_GLOB)}
|
|
254
|
+
- #{File.join(GraphWeaver.fragments_paths.first, GraphWeaver::Codegen::DOCUMENT_GLOB)}
|
|
255
|
+
YAML
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|