graph_weaver 0.7.7 → 0.7.8
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/Gemfile.lock +46 -46
- data/docs/getting_started.md +9 -0
- data/docs/upgrading.md +21 -1
- data/examples/github/generated/star_mutation.rb +1 -1
- data/examples/github/generated/stargazers_query.rb +1 -1
- data/examples/github/generated/starred_query.rb +1 -1
- data/lib/graph_weaver/client.rb +16 -4
- data/lib/graph_weaver/codegen.rb +42 -10
- data/lib/graph_weaver/internal.rb +16 -2
- data/lib/graph_weaver/schema_loader.rb +58 -9
- data/lib/graph_weaver/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd9b2703206fc3145399e11794cd488a5e3c3734584ca7a56fa0e4d2afed9a77
|
|
4
|
+
data.tar.gz: 0e247a7b925ff0a89cb5952ac962b31545af417f865342a22b8019dcbcdbd135
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7310d664c88a34de5fbdf9768e6a697ca6565e5ccd4c407cfea31c772f0f44001ceb1145a7e1837d1a46bcc74c16902c55c88d2be014df9c423bb2355dc882a0
|
|
7
|
+
data.tar.gz: abc81fbb21a1f3f26d8a7c93f1f660347e9f68088a833b37b6a66dcf05f17e6bb0bec95fc9432c6e99192ac3682e7961936d9e08b3f518b4af9e013623ef3f6c
|
data/Gemfile.lock
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
graph_weaver (0.7.
|
|
4
|
+
graph_weaver (0.7.8)
|
|
5
5
|
graphql (>= 2.6.7)
|
|
6
6
|
sorbet-runtime
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
actionpack (8.1.
|
|
12
|
-
actionview (= 8.1.
|
|
13
|
-
activesupport (= 8.1.
|
|
11
|
+
actionpack (8.1.4)
|
|
12
|
+
actionview (= 8.1.4)
|
|
13
|
+
activesupport (= 8.1.4)
|
|
14
14
|
nokogiri (>= 1.8.5)
|
|
15
15
|
rack (>= 2.2.4)
|
|
16
16
|
rack-session (>= 1.0.1)
|
|
@@ -18,13 +18,13 @@ GEM
|
|
|
18
18
|
rails-dom-testing (~> 2.2)
|
|
19
19
|
rails-html-sanitizer (~> 1.6)
|
|
20
20
|
useragent (~> 0.16)
|
|
21
|
-
actionview (8.1.
|
|
22
|
-
activesupport (= 8.1.
|
|
21
|
+
actionview (8.1.4)
|
|
22
|
+
activesupport (= 8.1.4)
|
|
23
23
|
builder (~> 3.1)
|
|
24
24
|
erubi (~> 1.11)
|
|
25
25
|
rails-dom-testing (~> 2.2)
|
|
26
26
|
rails-html-sanitizer (~> 1.6)
|
|
27
|
-
activesupport (8.1.
|
|
27
|
+
activesupport (8.1.4)
|
|
28
28
|
base64
|
|
29
29
|
bigdecimal
|
|
30
30
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -45,9 +45,9 @@ GEM
|
|
|
45
45
|
ast (2.4.3)
|
|
46
46
|
base64 (0.3.0)
|
|
47
47
|
benchmark (0.5.0)
|
|
48
|
-
bigdecimal (4.1.
|
|
48
|
+
bigdecimal (4.1.3)
|
|
49
49
|
builder (3.3.0)
|
|
50
|
-
concurrent-ruby (1.3.
|
|
50
|
+
concurrent-ruby (1.3.8)
|
|
51
51
|
connection_pool (3.0.2)
|
|
52
52
|
crack (1.0.1)
|
|
53
53
|
bigdecimal
|
|
@@ -58,7 +58,7 @@ GEM
|
|
|
58
58
|
reline (>= 0.3.8)
|
|
59
59
|
diff-lcs (1.6.2)
|
|
60
60
|
drb (2.2.3)
|
|
61
|
-
erb (6.0.
|
|
61
|
+
erb (6.0.7)
|
|
62
62
|
erubi (1.13.1)
|
|
63
63
|
faker (3.8.0)
|
|
64
64
|
i18n (>= 1.8.11, < 2)
|
|
@@ -81,14 +81,14 @@ GEM
|
|
|
81
81
|
google-protobuf (4.35.1-x86_64-linux-gnu)
|
|
82
82
|
bigdecimal
|
|
83
83
|
rake (~> 13.3)
|
|
84
|
-
graphql (2.6.
|
|
84
|
+
graphql (2.6.11)
|
|
85
85
|
base64
|
|
86
86
|
fiber-storage
|
|
87
87
|
logger
|
|
88
88
|
hashdiff (1.2.1)
|
|
89
89
|
i18n (1.15.2)
|
|
90
90
|
concurrent-ruby (~> 1.0)
|
|
91
|
-
io-console (0.
|
|
91
|
+
io-console (0.9.4)
|
|
92
92
|
irb (1.18.0)
|
|
93
93
|
pp (>= 0.6.0)
|
|
94
94
|
prism (>= 1.3.0)
|
|
@@ -149,9 +149,9 @@ GEM
|
|
|
149
149
|
rails-html-sanitizer (1.7.1)
|
|
150
150
|
loofah (~> 2.25, >= 2.25.2)
|
|
151
151
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
152
|
-
railties (8.1.
|
|
153
|
-
actionpack (= 8.1.
|
|
154
|
-
activesupport (= 8.1.
|
|
152
|
+
railties (8.1.4)
|
|
153
|
+
actionpack (= 8.1.4)
|
|
154
|
+
activesupport (= 8.1.4)
|
|
155
155
|
irb (~> 1.13)
|
|
156
156
|
rackup (>= 1.0.0)
|
|
157
157
|
rake (>= 12.2)
|
|
@@ -166,14 +166,14 @@ GEM
|
|
|
166
166
|
logger
|
|
167
167
|
prism (>= 1.6.0)
|
|
168
168
|
tsort
|
|
169
|
-
rdoc (8.
|
|
169
|
+
rdoc (8.1.0)
|
|
170
170
|
erb
|
|
171
171
|
prism (>= 1.6.0)
|
|
172
172
|
rbs (>= 4.0.0)
|
|
173
173
|
tsort
|
|
174
174
|
redcarpet (3.6.1)
|
|
175
175
|
regexp_parser (2.12.0)
|
|
176
|
-
reline (0.
|
|
176
|
+
reline (0.7.0)
|
|
177
177
|
io-console (~> 0.5)
|
|
178
178
|
require-hooks (0.5.1)
|
|
179
179
|
rexml (3.4.4)
|
|
@@ -195,16 +195,16 @@ GEM
|
|
|
195
195
|
rubydex (0.4.1-x86_64-darwin)
|
|
196
196
|
rubydex (0.4.1-x86_64-linux)
|
|
197
197
|
securerandom (0.4.1)
|
|
198
|
-
simplecov (1.3.
|
|
199
|
-
sorbet (0.6.
|
|
200
|
-
sorbet-static (= 0.6.
|
|
201
|
-
sorbet-runtime (0.6.
|
|
202
|
-
sorbet-static (0.6.
|
|
203
|
-
sorbet-static (0.6.
|
|
204
|
-
sorbet-static (0.6.
|
|
205
|
-
sorbet-static-and-runtime (0.6.
|
|
206
|
-
sorbet (= 0.6.
|
|
207
|
-
sorbet-runtime (= 0.6.
|
|
198
|
+
simplecov (1.3.1)
|
|
199
|
+
sorbet (0.6.13508)
|
|
200
|
+
sorbet-static (= 0.6.13508)
|
|
201
|
+
sorbet-runtime (0.6.13508)
|
|
202
|
+
sorbet-static (0.6.13508-aarch64-linux)
|
|
203
|
+
sorbet-static (0.6.13508-universal-darwin)
|
|
204
|
+
sorbet-static (0.6.13508-x86_64-linux)
|
|
205
|
+
sorbet-static-and-runtime (0.6.13508)
|
|
206
|
+
sorbet (= 0.6.13508)
|
|
207
|
+
sorbet-runtime (= 0.6.13508)
|
|
208
208
|
spoom (1.8.7)
|
|
209
209
|
erubi (>= 1.10.0)
|
|
210
210
|
prism (>= 0.28.0)
|
|
@@ -272,24 +272,24 @@ DEPENDENCIES
|
|
|
272
272
|
yard
|
|
273
273
|
|
|
274
274
|
CHECKSUMS
|
|
275
|
-
actionpack (8.1.
|
|
276
|
-
actionview (8.1.
|
|
277
|
-
activesupport (8.1.
|
|
275
|
+
actionpack (8.1.4) sha256=245493cc51be195a7645e6fc184880fa7e5b32ff1a08e053ee31f58c76e30ea4
|
|
276
|
+
actionview (8.1.4) sha256=eff1efd07c4d969abad288322b423bcb67fdbab5e324c91ca37352b80f226076
|
|
277
|
+
activesupport (8.1.4) sha256=891dd3fe43c4fc8391934b237b1189bb1f661c932a84a840043db9d46a4e80df
|
|
278
278
|
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
|
279
279
|
apollo-federation (3.10.3) sha256=61e6d8e1fd695ee45bab5d84f86f586d99c296ad77a86c6f1e7b964ac17be3d7
|
|
280
280
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
281
281
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
282
282
|
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
|
|
283
|
-
bigdecimal (4.1.
|
|
283
|
+
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
|
|
284
284
|
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
|
285
|
-
concurrent-ruby (1.3.
|
|
285
|
+
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
|
|
286
286
|
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
287
287
|
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
|
|
288
288
|
crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
|
|
289
289
|
debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
|
|
290
290
|
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
291
291
|
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
292
|
-
erb (6.0.
|
|
292
|
+
erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
|
|
293
293
|
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
|
294
294
|
faker (3.8.0) sha256=c147b308df73a90f27a4fc84f18d4c22ef0ad9c2a64b2b61c86fd0ca71753efc
|
|
295
295
|
faraday (2.14.4) sha256=9bb4408c44621b0dbaaaa39e5dbfc4dbe58cc6751b6eb94d16ae9e1f2f2fab6e
|
|
@@ -299,11 +299,11 @@ CHECKSUMS
|
|
|
299
299
|
google-protobuf (4.35.1-arm64-darwin) sha256=d9c957df04fa89c749fa9a72a7b383eb4296efc9b2303dc6fd6fbe39c698ad6b
|
|
300
300
|
google-protobuf (4.35.1-x86_64-darwin) sha256=66b62b4df00931018a692806df66393efa960d6d2b7da69735187249f950d3ee
|
|
301
301
|
google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd
|
|
302
|
-
graph_weaver (0.7.
|
|
303
|
-
graphql (2.6.
|
|
302
|
+
graph_weaver (0.7.8)
|
|
303
|
+
graphql (2.6.11) sha256=9a9cde910e805c4c08fdf8d22bf129e935affc9ca8b4f9b2ec26b4b63666b15d
|
|
304
304
|
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
|
|
305
305
|
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
|
|
306
|
-
io-console (0.
|
|
306
|
+
io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
|
|
307
307
|
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
308
308
|
json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
|
|
309
309
|
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
@@ -330,14 +330,14 @@ CHECKSUMS
|
|
|
330
330
|
rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
|
|
331
331
|
rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
|
|
332
332
|
rails-html-sanitizer (1.7.1) sha256=e797a7c9b01e567307e317c576b49ab4168017e63eea4dba9ce3cb587e2f22c2
|
|
333
|
-
railties (8.1.
|
|
333
|
+
railties (8.1.4) sha256=b5e7a8e8984e33114d68af62227971960aae1e6c02bd7c9d826ffad97fff583c
|
|
334
334
|
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
335
335
|
rbi (0.4.3) sha256=cc090ae62c3246412b992954f54077d6046c9b04f1537a4d24bfae8714503eac
|
|
336
336
|
rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
|
|
337
|
-
rdoc (8.
|
|
337
|
+
rdoc (8.1.0) sha256=befc37880a5cfd36dcb176c748b5fd5ee39dd3536a75e81824e4972ce230f60a
|
|
338
338
|
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
|
|
339
339
|
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
|
340
|
-
reline (0.
|
|
340
|
+
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
|
|
341
341
|
require-hooks (0.5.1) sha256=49a6b14518b91c5b8006d0644ef257af0ca033df23625bd4e314a8c5282191d9
|
|
342
342
|
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
343
343
|
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
@@ -350,13 +350,13 @@ CHECKSUMS
|
|
|
350
350
|
rubydex (0.4.1-x86_64-darwin) sha256=a9681db5bc9be6a62f226d96d6889b6b600d0b09e7576391702aafd29912145b
|
|
351
351
|
rubydex (0.4.1-x86_64-linux) sha256=4e2c8398c9fc585485b54edd28f5ecf76b5a02255e177b6f8402e9d2afffd51b
|
|
352
352
|
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
|
353
|
-
simplecov (1.3.
|
|
354
|
-
sorbet (0.6.
|
|
355
|
-
sorbet-runtime (0.6.
|
|
356
|
-
sorbet-static (0.6.
|
|
357
|
-
sorbet-static (0.6.
|
|
358
|
-
sorbet-static (0.6.
|
|
359
|
-
sorbet-static-and-runtime (0.6.
|
|
353
|
+
simplecov (1.3.1) sha256=b326d38e819053499aa2ebe35d544d8bd780eba143a50f48a624b450ad87feec
|
|
354
|
+
sorbet (0.6.13508) sha256=de67c75424731d39ac529639ff6849c3fe33d91c94608765d912d35efa5941a2
|
|
355
|
+
sorbet-runtime (0.6.13508) sha256=011ee266adead459077ebc0058d36aeb75d6c90968db8524916050ce3c62e526
|
|
356
|
+
sorbet-static (0.6.13508-aarch64-linux) sha256=1c54603399d40d78d6e73c853c5e0b6dcfc34105a92f6630f4a5329d1e6ed3c0
|
|
357
|
+
sorbet-static (0.6.13508-universal-darwin) sha256=65280392d193ca85828b13d07bb8eb90f41ad57f8d9bc79c5edcd078aa7dc308
|
|
358
|
+
sorbet-static (0.6.13508-x86_64-linux) sha256=57fd620b1a747baad432ef5c2bc2cd6218664e00626fc4e7fb5ed5f633e3a286
|
|
359
|
+
sorbet-static-and-runtime (0.6.13508) sha256=874d9fe5d98eebddd322629ee3d27021a6da32594edfa170295c0b4bcd285d66
|
|
360
360
|
spoom (1.8.7) sha256=81960502cdf23aa991f015988aa5dd43325e23ae4318b8e00d541299cbdb70f6
|
|
361
361
|
tapioca (0.20.0) sha256=b375ed7bf51d2c86c09543eafedd0de85513634187c0f7a062615af8bb9347a0
|
|
362
362
|
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
data/docs/getting_started.md
CHANGED
|
@@ -466,6 +466,15 @@ db/schema.graphql records no source url and the graph names no server behind it
|
|
|
466
466
|
still exits 1, because a gate that passes on having compared nothing is worse
|
|
467
467
|
than one that says so.
|
|
468
468
|
|
|
469
|
+
**A large dump costs nothing until something asks for it.** A client built from
|
|
470
|
+
a file stats the path at construction — a typo fails at boot, naming it — and
|
|
471
|
+
parses the file the first time anything wants the schema: codegen, a test mode,
|
|
472
|
+
`check_query`. Executing a generated module never does, so a production boot
|
|
473
|
+
never parses.
|
|
474
|
+
|
|
475
|
+
Keep one client per server rather than building one per call — a client parses
|
|
476
|
+
its dump once and keeps the schema.
|
|
477
|
+
|
|
469
478
|
## More than one schema
|
|
470
479
|
|
|
471
480
|
The five steps above describe one graph — a schema, its queries, its output. An
|
data/docs/upgrading.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Upgrading
|
|
2
2
|
|
|
3
3
|
[Regenerate](#regenerate-on-every-upgrade) whichever version you're on, then read
|
|
4
|
-
the one section that is yours: from [0.7.
|
|
4
|
+
the one section that is yours: from [0.7.7](#upgrading-from-077), from
|
|
5
|
+
[0.7.6](#upgrading-from-076), from
|
|
5
6
|
[0.7.5](#upgrading-from-075), from
|
|
6
7
|
[0.7.4](#upgrading-from-074), from [0.7.3](#upgrading-from-073), from
|
|
7
8
|
[0.7.1](#upgrading-from-071), from [0.7.0](#upgrading-from-070) or from
|
|
@@ -25,6 +26,25 @@ That's the reminder working, not a false alarm. Generation is deterministic, so
|
|
|
25
26
|
the diff is exactly what the new version emits differently and nothing else —
|
|
26
27
|
worth reading rather than rubber-stamping.
|
|
27
28
|
|
|
29
|
+
## Upgrading from 0.7.7
|
|
30
|
+
|
|
31
|
+
A patch release about load time. Nothing in it reaches an app that reads its
|
|
32
|
+
dump and generates; one edge moved, below. Read the left column and skip what
|
|
33
|
+
isn't yours; the [changelog](../CHANGELOG.md) says why each one moved.
|
|
34
|
+
|
|
35
|
+
| applies if you… | what changed |
|
|
36
|
+
|---|---|
|
|
37
|
+
| build a client from a schema dump — `GraphWeaver.new("schema.graphql")`, or a graph naming one | it is parsed on first use, not at construction, so a production boot never parses it. A missing or unreadable path still refuses at construction, naming it; a file that exists but isn't a schema refuses at the first `schema`, `parse` or `check_query`. `rake graph_weaver:verify` catches that one in CI |
|
|
38
|
+
| call `client.check_query` per query against a supergraph client | the routing table is built once per file version rather than per call — nothing to change, ~200 ms back per call on a 2000-type supergraph |
|
|
39
|
+
| generate anonymous operations whose comments carry non-ASCII text, or check out with CRLF | they generate under any parser graphql-ruby is configured with; nothing to change unless you had named an operation to dodge a "could not name the anonymous operation" refusal |
|
|
40
|
+
|
|
41
|
+
Then regenerate, and the gate:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
rake graph_weaver:generate
|
|
45
|
+
rake graph_weaver:verify
|
|
46
|
+
```
|
|
47
|
+
|
|
28
48
|
## Upgrading from 0.7.6
|
|
29
49
|
|
|
30
50
|
Read the left column and skip what isn't yours; the
|
data/lib/graph_weaver/client.rb
CHANGED
|
@@ -75,8 +75,18 @@ class GraphWeaver::Client
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# a live schema class doubles as an in-process transport; a loaded
|
|
78
|
-
# dump has no resolvers, so it is type information only
|
|
79
|
-
|
|
78
|
+
# dump has no resolvers, so it is type information only — and it is
|
|
79
|
+
# read on first use, the rule a url client already follows. An app
|
|
80
|
+
# that builds one only to name its supergraph (what `graphql: :router`
|
|
81
|
+
# plans against) then never parses the file at all.
|
|
82
|
+
if source.is_a?(Module)
|
|
83
|
+
@schema = source
|
|
84
|
+
else
|
|
85
|
+
# a path is checked now — a stat, not a parse — so a wrong one
|
|
86
|
+
# refuses where it was written rather than at the first request
|
|
87
|
+
GraphWeaver::SchemaLoader.check_path!(source) unless GraphWeaver::SchemaLoader.content?(source)
|
|
88
|
+
@dump = source
|
|
89
|
+
end
|
|
80
90
|
# A supergraph's routing table lives in the file, not in the loaded
|
|
81
91
|
# schema, so the path is the only thing that can name one later. Told
|
|
82
92
|
# from SDL by its extension, as SchemaLoader tells it.
|
|
@@ -147,8 +157,9 @@ class GraphWeaver::Client
|
|
|
147
157
|
FAILURE_TTL = 1.0
|
|
148
158
|
private_constant :FAILURE_TTL
|
|
149
159
|
|
|
150
|
-
# The schema,
|
|
151
|
-
# per the client's cache:/ttl:)
|
|
160
|
+
# The schema, on first use: a dump is read and built, a url is
|
|
161
|
+
# introspected through the transport (cached per the client's cache:/ttl:).
|
|
162
|
+
# A schema class is the schema, and was there all along.
|
|
152
163
|
#
|
|
153
164
|
# Locked because a cold Puma process serves its first requests
|
|
154
165
|
# concurrently: a bare ||= there is one full introspection round trip per
|
|
@@ -156,6 +167,7 @@ class GraphWeaver::Client
|
|
|
156
167
|
def schema
|
|
157
168
|
@schema_lock.synchronize do
|
|
158
169
|
next @schema if @schema
|
|
170
|
+
next @schema = GraphWeaver::SchemaLoader.load(@dump) if @dump
|
|
159
171
|
raise @schema_error if @schema_error && Process.clock_gettime(Process::CLOCK_MONOTONIC) < @schema_error_until
|
|
160
172
|
|
|
161
173
|
begin
|
data/lib/graph_weaver/codegen.rb
CHANGED
|
@@ -458,19 +458,51 @@ class GraphWeaver::Codegen
|
|
|
458
458
|
declares!(named, name)
|
|
459
459
|
end
|
|
460
460
|
|
|
461
|
-
#
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
#
|
|
466
|
-
#
|
|
461
|
+
# What an operation definition opens with.
|
|
462
|
+
OPERATION_START = %i[QUERY MUTATION SUBSCRIPTION LCURLY].to_set.freeze
|
|
463
|
+
private_constant :OPERATION_START
|
|
464
|
+
|
|
465
|
+
# Where the operation starts in @query, in characters — lexed, not derived
|
|
466
|
+
# from a reported position, because no two parsers report the same one:
|
|
467
|
+
# graphql-ruby's own gives a BYTE position minus a CHARACTER line start
|
|
468
|
+
# (Language::Parser#column_at), its C parser gives a true column and counts
|
|
469
|
+
# \r\n as two lines. Language::Lexer is the same implementation whichever
|
|
470
|
+
# parser is default, and its #pos is an exact byte offset, so nothing here is
|
|
471
|
+
# left to interpret.
|
|
472
|
+
#
|
|
473
|
+
# The operation is the first one of those tokens outside every fragment
|
|
474
|
+
# definition, selection set and argument list — an argument's braces are
|
|
475
|
+
# not a body, and a fragment's body is not the operation's.
|
|
467
476
|
def operation_offset
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
477
|
+
lexer = GraphQL::Language::Lexer.new(@query)
|
|
478
|
+
depth = 0
|
|
479
|
+
parens = 0
|
|
480
|
+
fragment = T.let(false, T::Boolean)
|
|
481
|
+
|
|
482
|
+
while (kind = lexer.advance)
|
|
483
|
+
parens += 1 if kind == :LPAREN
|
|
484
|
+
parens -= 1 if kind == :RPAREN
|
|
485
|
+
next unless parens.zero?
|
|
486
|
+
|
|
487
|
+
if depth.zero? && !fragment && OPERATION_START.include?(kind)
|
|
488
|
+
return @query.byteslice(0, lexer.pos).length
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
case kind
|
|
492
|
+
when :LCURLY
|
|
493
|
+
depth += 1
|
|
494
|
+
fragment = false
|
|
495
|
+
when :RCURLY then depth -= 1
|
|
496
|
+
when :FRAGMENT then fragment = true if depth.zero?
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# The lexer and the parser disagree about what this document holds. Splice
|
|
501
|
+
# past the end, which declares! can only refuse.
|
|
502
|
+
@query.length
|
|
471
503
|
end
|
|
472
504
|
|
|
473
|
-
# The splice above
|
|
505
|
+
# The splice above lands on an offset this file worked out for itself, so it
|
|
474
506
|
# can land wrong and still emit a module that looks fine — and OPERATION_NAME
|
|
475
507
|
# would then be an operationName the document doesn't declare, which every
|
|
476
508
|
# server rejects. Nothing else re-reads the query, so this is the only place
|
|
@@ -375,10 +375,24 @@ module GraphWeaver
|
|
|
375
375
|
path = path.to_path if path.respond_to?(:to_path)
|
|
376
376
|
return unless path&.end_with?(".graphql", ".gql")
|
|
377
377
|
|
|
378
|
-
|
|
379
|
-
|
|
378
|
+
# once per file version: check_query is called per query, and the
|
|
379
|
+
# table behind a 2000-type supergraph costs ~200 ms to read against
|
|
380
|
+
# a fraction of a millisecond to stat — mtime and size in the key
|
|
381
|
+
# are what let schema:refresh's rewrite be seen without a reload
|
|
382
|
+
stat = File.stat(path)
|
|
383
|
+
key = [File.expand_path(path), stat.mtime, stat.size]
|
|
384
|
+
TABLES.synchronize do
|
|
385
|
+
return TABLES_BY_FILE[key] if TABLES_BY_FILE.key?(key)
|
|
386
|
+
|
|
387
|
+
sdl = File.read(path)
|
|
388
|
+
TABLES_BY_FILE[key] = SchemaLoader.routing_table(sdl) if SchemaLoader.federation_sdl?(sdl)
|
|
389
|
+
end
|
|
380
390
|
end
|
|
381
391
|
|
|
392
|
+
TABLES = Mutex.new
|
|
393
|
+
TABLES_BY_FILE = {}
|
|
394
|
+
private_constant :TABLES, :TABLES_BY_FILE
|
|
395
|
+
|
|
382
396
|
private
|
|
383
397
|
|
|
384
398
|
# Which subgraphs a validation error is about, on a federated schema:
|
|
@@ -49,6 +49,14 @@ module GraphWeaver::SchemaLoader
|
|
|
49
49
|
source.match?(SDL_CONTENT)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Schema content rather than a path: introspection JSON, SDL, or anything
|
|
53
|
+
# spanning lines (which `load` refuses as content it doesn't recognize).
|
|
54
|
+
def self.content?(source)
|
|
55
|
+
source = source.to_path if source.respond_to?(:to_path)
|
|
56
|
+
source = source.to_s
|
|
57
|
+
source.lstrip.start_with?("{") || sdl_content?(source) || source.include?("\n")
|
|
58
|
+
end
|
|
59
|
+
|
|
52
60
|
def self.load_path(path)
|
|
53
61
|
case File.extname(path)
|
|
54
62
|
when ".json"
|
|
@@ -56,13 +64,33 @@ module GraphWeaver::SchemaLoader
|
|
|
56
64
|
when ".graphql", ".gql"
|
|
57
65
|
build_sdl(read_schema(path))
|
|
58
66
|
else
|
|
59
|
-
|
|
60
|
-
"unsupported schema format: #{truncate(path)} — expected a .json (introspection) or " \
|
|
61
|
-
".graphql/.gql (SDL) path, or the content itself"
|
|
67
|
+
unsupported_path!(path)
|
|
62
68
|
end
|
|
63
69
|
end
|
|
64
70
|
private_class_method :load_path
|
|
65
71
|
|
|
72
|
+
# Fail fast on a path `load` would refuse later — the extension and a
|
|
73
|
+
# stat, no read and no parse. A client defers building the schema to first
|
|
74
|
+
# use, so this is what still refuses at the line that named the file: a
|
|
75
|
+
# wrong path is known at boot, a malformed file at first use.
|
|
76
|
+
def self.check_path!(path)
|
|
77
|
+
path = path.to_path if path.respond_to?(:to_path)
|
|
78
|
+
unsupported_path!(path) unless dump_path?(path)
|
|
79
|
+
|
|
80
|
+
resolved = GraphWeaver::Internal::Util.resolve(path)
|
|
81
|
+
File.stat(resolved)
|
|
82
|
+
raise GraphWeaver::Error, "can't read the schema at #{path}: permission denied" unless File.readable?(resolved)
|
|
83
|
+
rescue SystemCallError => e
|
|
84
|
+
raise GraphWeaver::Error, "can't read the schema at #{path}: #{e.message}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.unsupported_path!(path)
|
|
88
|
+
raise GraphWeaver::Error, url_error(path) ||
|
|
89
|
+
"unsupported schema format: #{truncate(path)} — expected a .json (introspection) or " \
|
|
90
|
+
".graphql/.gql (SDL) path, or the content itself"
|
|
91
|
+
end
|
|
92
|
+
private_class_method :unsupported_path!
|
|
93
|
+
|
|
66
94
|
def self.read_schema(path)
|
|
67
95
|
File.read(GraphWeaver::Internal::Util.resolve(path))
|
|
68
96
|
rescue SystemCallError => e
|
|
@@ -123,10 +151,23 @@ module GraphWeaver::SchemaLoader
|
|
|
123
151
|
else sdl
|
|
124
152
|
end
|
|
125
153
|
|
|
126
|
-
build(kind) {
|
|
154
|
+
build(kind) { build_schema(prepared) }
|
|
127
155
|
end
|
|
128
156
|
private_class_method :build_sdl
|
|
129
157
|
|
|
158
|
+
# A supergraph arrives here as the AST strip_federation filtered, anything
|
|
159
|
+
# else as SDL text. `from_definition` is parse-then-build, so an AST takes
|
|
160
|
+
# the build half directly — printing it back and parsing it again is a
|
|
161
|
+
# round trip through text that can only cost. spec/schema_loader_spec.rb
|
|
162
|
+
# pins the entry point, so a graphql-ruby that moves it fails there rather
|
|
163
|
+
# than as a NoMethodError inside someone's load.
|
|
164
|
+
def self.build_schema(prepared)
|
|
165
|
+
return GraphQL::Schema.from_definition(prepared) if prepared.is_a?(String)
|
|
166
|
+
|
|
167
|
+
GraphQL::Schema::BuildFromDefinition.from_document(GraphQL::Schema, prepared, default_resolve: nil)
|
|
168
|
+
end
|
|
169
|
+
private_class_method :build_schema
|
|
170
|
+
|
|
130
171
|
def self.build_introspection(result)
|
|
131
172
|
build(:introspection) { GraphQL::Schema.from_introspection(result) }.tap do |schema|
|
|
132
173
|
restore_one_of!(schema, result)
|
|
@@ -519,9 +560,9 @@ module GraphWeaver::SchemaLoader
|
|
|
519
560
|
# join__*/link__* type and directive definitions, and every @join__*/@link
|
|
520
561
|
# application on the types that remain. What's left is the merged graph's
|
|
521
562
|
# ordinary type shapes — exactly what codegen reads. Parsing is lenient (it's
|
|
522
|
-
# schema *building* that rejects the join directives), so we parse
|
|
523
|
-
# AST, and
|
|
524
|
-
# and no join__* leaking into schema.types.
|
|
563
|
+
# schema *building* that rejects the join directives), so we parse and filter
|
|
564
|
+
# the AST, and hand that document to the builder — no graphql-ruby
|
|
565
|
+
# monkeypatch and no join__* leaking into schema.types.
|
|
525
566
|
def self.strip_federation(sdl)
|
|
526
567
|
doc = GraphQL.parse(sdl)
|
|
527
568
|
ns = link_namespaces(doc)
|
|
@@ -535,7 +576,7 @@ module GraphWeaver::SchemaLoader
|
|
|
535
576
|
"is the whole schema behind @inaccessible?"
|
|
536
577
|
end
|
|
537
578
|
|
|
538
|
-
GraphQL::Language::Nodes::Document.new(definitions: defs)
|
|
579
|
+
GraphQL::Language::Nodes::Document.new(definitions: defs)
|
|
539
580
|
end
|
|
540
581
|
private_class_method :strip_federation
|
|
541
582
|
|
|
@@ -1259,10 +1300,18 @@ module GraphWeaver::SchemaLoader
|
|
|
1259
1300
|
|
|
1260
1301
|
# A @key/@requires/@provides field set is a selection set. Flattened to
|
|
1261
1302
|
# dotted paths, so a nested one is recognizable as nested by its shape.
|
|
1303
|
+
# Memoized on the text: a supergraph repeats the same few spellings
|
|
1304
|
+
# thousands of times ("id" on every entity), and each was a parse.
|
|
1262
1305
|
def self.parse_field_set(text)
|
|
1263
|
-
|
|
1306
|
+
FIELD_SETS.synchronize do
|
|
1307
|
+
FIELD_SETS_BY_TEXT[text] ||= flatten(GraphQL.parse("{ #{text} }").definitions.first.selections, []).freeze
|
|
1308
|
+
end
|
|
1264
1309
|
end
|
|
1265
1310
|
|
|
1311
|
+
FIELD_SETS = Mutex.new
|
|
1312
|
+
FIELD_SETS_BY_TEXT = {}
|
|
1313
|
+
private_constant :FIELD_SETS, :FIELD_SETS_BY_TEXT
|
|
1314
|
+
|
|
1266
1315
|
def self.flatten(selections, prefix)
|
|
1267
1316
|
selections.flat_map do |node|
|
|
1268
1317
|
unless node.is_a?(GraphQL::Language::Nodes::Field)
|
data/lib/graph_weaver/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graph_weaver
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Pepper
|
|
@@ -364,7 +364,7 @@ licenses:
|
|
|
364
364
|
- MIT
|
|
365
365
|
metadata:
|
|
366
366
|
bug_tracker_uri: https://github.com/dpep/graph_weaver/issues
|
|
367
|
-
changelog_uri: https://github.com/dpep/graph_weaver/blob/v0.7.
|
|
367
|
+
changelog_uri: https://github.com/dpep/graph_weaver/blob/v0.7.8/CHANGELOG.md
|
|
368
368
|
documentation_uri: https://github.com/dpep/graph_weaver/tree/main/docs
|
|
369
369
|
rubygems_mfa_required: 'true'
|
|
370
370
|
source_code_uri: https://github.com/dpep/graph_weaver
|