@shortlink-org/portolan 0.1.0 → 0.2.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.
- package/README.md +69 -205
- package/cli/init.mjs +285 -0
- package/cli/init.test.mjs +158 -0
- package/cli/portolan.mjs +37 -73
- package/index.html +19 -0
- package/package.json +5 -10
- package/plugins/README.md +66 -27
- package/plugins/portolan-go.wasm +0 -0
- package/portolan.json +52 -73
- package/public/landing/cats/bug-hunter.webp +0 -0
- package/public/landing/cats/compass-nap.webp +0 -0
- package/public/landing/cats/diagrammer.webp +0 -0
- package/public/landing/cats/docs-reader.webp +0 -0
- package/public/landing/cats/laptop.webp +0 -0
- package/public/landing/cats/map-inspector.webp +0 -0
- package/public/landing/cats/server-break.webp +0 -0
- package/public/landing/cats/star-mapper.webp +0 -0
- package/public/landing/cats/system-builder.webp +0 -0
- package/public/landing/cats/thread-tangle.webp +0 -0
- package/public/og.png +0 -0
- package/public/readme-header.webp +0 -0
- package/schema/portolan.schema.json +180 -0
- package/scripts/builtin-plugins.mjs +14 -0
- package/scripts/delivery-presets.mjs +353 -0
- package/scripts/gen.mjs +37 -4
- package/scripts/history.mjs +126 -0
- package/scripts/history.test.mjs +110 -0
- package/scripts/host-plugins/fetch-bsr.mjs +351 -0
- package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
- package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
- package/scripts/host-plugins/fetch-csr.mjs +386 -0
- package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
- package/scripts/host-plugins/fetch-git.mjs +379 -0
- package/scripts/host-plugins/fetch-git.test.mjs +210 -0
- package/scripts/local-api.mjs +27 -10
- package/scripts/local-api.test.mjs +85 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +24 -3
- package/scripts/plugin-host.mjs +77 -18
- package/scripts/plugin-host.test.mjs +50 -1
- package/scripts/plugin-wasm-worker.mjs +6 -3
- package/scripts/run-builtin.mjs +6 -26
- package/scripts/schema.mjs +6 -1
- package/src/app/App.tsx +24 -429
- package/src/app/Breadcrumbs.test.ts +7 -0
- package/src/app/Breadcrumbs.tsx +15 -2
- package/src/app/CatalogApp.tsx +436 -0
- package/src/app/Sidebar.tsx +0 -1
- package/src/chat/ChatPanel.tsx +98 -41
- package/src/chat/Composer.tsx +4 -2
- package/src/chat/Conversation.tsx +79 -15
- package/src/chat/Header.tsx +43 -9
- package/src/chat/Starter.tsx +64 -3
- package/src/chat/Steps.tsx +1 -1
- package/src/chat/flags.test.ts +51 -14
- package/src/chat/flags.ts +8 -2
- package/src/chat/page-context.test.ts +51 -0
- package/src/chat/page-context.ts +128 -0
- package/src/chat/prompt.test.ts +49 -1
- package/src/chat/prompt.ts +42 -1
- package/src/chat/tools.ts +7 -2
- package/src/chat/transport.ts +16 -4
- package/src/components/ProblemRow.tsx +182 -0
- package/src/data.ts +20 -5
- package/src/er/ErCanvas.tsx +1 -1
- package/src/graph/DependencyGraph.tsx +10 -1
- package/src/graph/FocusedEventGraph.tsx +1 -1
- package/src/graph/GraphToolbar.tsx +2 -4
- package/src/index.css +220 -2
- package/src/landing/DraggableReveal.tsx +168 -0
- package/src/landing/EstateGraph.tsx +28 -0
- package/src/landing/FlowPlayback.tsx +282 -0
- package/src/landing/HeroMap.tsx +128 -0
- package/src/landing/LandingPage.tsx +757 -0
- package/src/landing/ProductFrame.tsx +68 -0
- package/src/landing/ProductTour.tsx +371 -0
- package/src/landing/catalog.ts +25 -0
- package/src/landing/motion.tsx +52 -0
- package/src/lib/all-problems.ts +28 -0
- package/src/lib/local-api.ts +35 -1
- package/src/lib/setup-info.test.ts +8 -0
- package/src/lib/setup-info.ts +3 -2
- package/src/lib/source-code.ts +2 -1
- package/src/likec4/C4View.tsx +5 -0
- package/src/likec4/InteractiveView.tsx +5 -1
- package/src/map/ContextMapGraph.tsx +47 -20
- package/src/pages/ContextMap.tsx +5 -1
- package/src/pages/FlowDetail.tsx +24 -7
- package/src/pages/Overview.tsx +68 -12
- package/src/pages/Problems.tsx +6 -193
- package/src/pages/Settings.tsx +133 -108
- package/src/pages/settings/DeliverySettings.tsx +229 -0
- package/src/pages/settings/PreferencesSettings.tsx +101 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +13 -1
- package/vite.config.ts +17 -9
- package/catalog/enum_test.go +0 -46
- package/catalog/model.go +0 -932
- package/catalog/roundtrip_test.go +0 -185
- package/catalog/via_test.go +0 -38
- package/go.mod +0 -14
- package/go.sum +0 -14
- package/internal/commands/cargo.go +0 -154
- package/internal/commands/commands.go +0 -143
- package/internal/commands/commands_test.go +0 -323
- package/internal/commands/gradle.go +0 -77
- package/internal/commands/justfile.go +0 -82
- package/internal/commands/makefile.go +0 -106
- package/internal/commands/maven.go +0 -93
- package/internal/commands/packagejson.go +0 -159
- package/internal/commands/pyproject.go +0 -169
- package/internal/commands/taskfile.go +0 -101
- package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
- package/internal/commands/testdata/estate/Makefile +0 -33
- package/internal/commands/testdata/estate/Taskfile.yml +0 -21
- package/internal/commands/testdata/estate/build.gradle.kts +0 -21
- package/internal/commands/testdata/estate/justfile +0 -20
- package/internal/commands/testdata/estate/package.json +0 -13
- package/internal/commands/testdata/estate/pom.xml +0 -29
- package/internal/commands/testdata/estate/pyproject.toml +0 -27
- package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
- package/internal/commands/testdata/golden/commands.json +0 -305
- package/internal/gohttp/analyze.go +0 -2500
- package/internal/gohttp/endpoints.go +0 -1067
- package/internal/gohttp/roots.go +0 -320
- package/internal/gohttp/typed.go +0 -143
- package/internal/goscan/constants.go +0 -85
- package/internal/goscan/goscan_test.go +0 -227
- package/internal/goscan/names.go +0 -52
- package/internal/goscan/parse_test.go +0 -11
- package/internal/goscan/source.go +0 -36
- package/internal/goscan/tree.go +0 -155
- package/internal/goscan/types.go +0 -99
- package/internal/wsdl/ids.go +0 -127
- package/internal/wsdl/ids_test.go +0 -21
- package/internal/wsdl/model.go +0 -62
- package/internal/wsdl/parse.go +0 -920
- package/internal/wsdl/parse_test.go +0 -133
- package/plugin/describe.go +0 -107
- package/plugin/describe_test.go +0 -114
- package/plugin/protocol.go +0 -117
- package/plugin/schematest/schematest.go +0 -126
- package/plugins/extract-adr/describe.go +0 -19
- package/plugins/extract-adr/describe_test.go +0 -11
- package/plugins/extract-adr/extract.go +0 -153
- package/plugins/extract-adr/extract_test.go +0 -350
- package/plugins/extract-adr/history.go +0 -99
- package/plugins/extract-adr/main.go +0 -65
- package/plugins/extract-adr/parse.go +0 -642
- package/plugins/extract-adr/parse_test.go +0 -405
- package/plugins/extract-asyncapi/describe.go +0 -19
- package/plugins/extract-asyncapi/describe_test.go +0 -11
- package/plugins/extract-asyncapi/extract.go +0 -315
- package/plugins/extract-asyncapi/extract_test.go +0 -197
- package/plugins/extract-asyncapi/main.go +0 -48
- package/plugins/extract-asyncapi/spec.go +0 -151
- package/plugins/extract-commands/describe.go +0 -19
- package/plugins/extract-commands/describe_test.go +0 -11
- package/plugins/extract-commands/extract.go +0 -79
- package/plugins/extract-commands/extract_test.go +0 -92
- package/plugins/extract-commands/main.go +0 -55
- package/plugins/extract-csr/avro.go +0 -258
- package/plugins/extract-csr/describe.go +0 -19
- package/plugins/extract-csr/describe_test.go +0 -11
- package/plugins/extract-csr/extract.go +0 -338
- package/plugins/extract-csr/extract_test.go +0 -374
- package/plugins/extract-csr/jsonschema.go +0 -343
- package/plugins/extract-csr/lock.go +0 -25
- package/plugins/extract-csr/main.go +0 -84
- package/plugins/extract-csr/subject.go +0 -102
- package/plugins/extract-flows/describe.go +0 -19
- package/plugins/extract-flows/describe_test.go +0 -11
- package/plugins/extract-flows/extract.go +0 -91
- package/plugins/extract-flows/main.go +0 -45
- package/plugins/extract-flows/parse.go +0 -593
- package/plugins/extract-flows/parse_test.go +0 -204
- package/plugins/extract-glossary/describe.go +0 -19
- package/plugins/extract-glossary/describe_test.go +0 -11
- package/plugins/extract-glossary/extract.go +0 -115
- package/plugins/extract-glossary/extract_test.go +0 -220
- package/plugins/extract-glossary/main.go +0 -59
- package/plugins/extract-glossary/parse.go +0 -214
- package/plugins/extract-glossary/parse_test.go +0 -203
- package/plugins/extract-go/aggregate.go +0 -214
- package/plugins/extract-go/client.go +0 -409
- package/plugins/extract-go/client_test.go +0 -266
- package/plugins/extract-go/describe.go +0 -19
- package/plugins/extract-go/describe_test.go +0 -11
- package/plugins/extract-go/enum.go +0 -195
- package/plugins/extract-go/enum_test.go +0 -82
- package/plugins/extract-go/event.go +0 -99
- package/plugins/extract-go/extract.go +0 -191
- package/plugins/extract-go/extract_test.go +0 -261
- package/plugins/extract-go/flow.go +0 -1441
- package/plugins/extract-go/flow_test.go +0 -609
- package/plugins/extract-go/httpclient.go +0 -174
- package/plugins/extract-go/httpclient_test.go +0 -296
- package/plugins/extract-go/ids.go +0 -92
- package/plugins/extract-go/layout.go +0 -225
- package/plugins/extract-go/layout_test.go +0 -125
- package/plugins/extract-go/lifecycle.go +0 -324
- package/plugins/extract-go/lifecycle_test.go +0 -108
- package/plugins/extract-go/main.go +0 -79
- package/plugins/extract-go/operation.go +0 -157
- package/plugins/extract-go/source.go +0 -316
- package/plugins/extract-go/transport.go +0 -321
- package/plugins/extract-go/transport_test.go +0 -145
- package/plugins/extract-go/wiring.go +0 -434
- package/plugins/extract-go-nats/describe.go +0 -19
- package/plugins/extract-go-nats/describe_test.go +0 -11
- package/plugins/extract-go-nats/extract.go +0 -177
- package/plugins/extract-go-nats/extract_test.go +0 -389
- package/plugins/extract-go-nats/index.go +0 -379
- package/plugins/extract-go-nats/main.go +0 -42
- package/plugins/extract-go-nats/resolve.go +0 -161
- package/plugins/extract-go-nats/sites.go +0 -224
- package/plugins/extract-graphql/describe.go +0 -19
- package/plugins/extract-graphql/describe_test.go +0 -11
- package/plugins/extract-graphql/extract.go +0 -433
- package/plugins/extract-graphql/extract_test.go +0 -256
- package/plugins/extract-graphql/ids.go +0 -49
- package/plugins/extract-graphql/lex.go +0 -237
- package/plugins/extract-graphql/main.go +0 -51
- package/plugins/extract-graphql/parse.go +0 -621
- package/plugins/extract-graphql/parse_test.go +0 -122
- package/plugins/extract-http-clients/describe.go +0 -19
- package/plugins/extract-http-clients/describe_test.go +0 -11
- package/plugins/extract-http-clients/extract.go +0 -705
- package/plugins/extract-http-clients/extract_test.go +0 -1263
- package/plugins/extract-http-clients/main.go +0 -42
- package/plugins/extract-openapi/describe.go +0 -19
- package/plugins/extract-openapi/describe_test.go +0 -11
- package/plugins/extract-openapi/discover.go +0 -243
- package/plugins/extract-openapi/extract.go +0 -526
- package/plugins/extract-openapi/extract_test.go +0 -545
- package/plugins/extract-openapi/main.go +0 -75
- package/plugins/extract-openapi/spec.go +0 -350
- package/plugins/extract-project/describe.go +0 -19
- package/plugins/extract-project/describe_test.go +0 -11
- package/plugins/extract-project/extract.go +0 -221
- package/plugins/extract-project/extract_test.go +0 -109
- package/plugins/extract-project/main.go +0 -41
- package/plugins/extract-proto/ast.go +0 -125
- package/plugins/extract-proto/consumes.go +0 -77
- package/plugins/extract-proto/describe.go +0 -19
- package/plugins/extract-proto/extract.go +0 -293
- package/plugins/extract-proto/extract_test.go +0 -459
- package/plugins/extract-proto/ids.go +0 -89
- package/plugins/extract-proto/ids_test.go +0 -57
- package/plugins/extract-proto/lex.go +0 -285
- package/plugins/extract-proto/main.go +0 -100
- package/plugins/extract-proto/module.go +0 -120
- package/plugins/extract-proto/parse.go +0 -720
- package/plugins/extract-proto/parse_test.go +0 -307
- package/plugins/extract-proto/provides.go +0 -236
- package/plugins/extract-proto/resolve.go +0 -222
- package/plugins/extract-proto/resolve_test.go +0 -119
- package/plugins/extract-redis/describe.go +0 -19
- package/plugins/extract-redis/describe_test.go +0 -11
- package/plugins/extract-redis/extract.go +0 -183
- package/plugins/extract-redis/extract_test.go +0 -168
- package/plugins/extract-redis/keyspaces.go +0 -469
- package/plugins/extract-redis/main.go +0 -44
- package/plugins/extract-river/describe.go +0 -19
- package/plugins/extract-river/describe_test.go +0 -11
- package/plugins/extract-river/extract.go +0 -507
- package/plugins/extract-river/extract_test.go +0 -132
- package/plugins/extract-river/main.go +0 -42
- package/plugins/extract-sql/ddl.go +0 -893
- package/plugins/extract-sql/ddl_test.go +0 -401
- package/plugins/extract-sql/describe.go +0 -19
- package/plugins/extract-sql/describe_test.go +0 -11
- package/plugins/extract-sql/layout.go +0 -221
- package/plugins/extract-sql/layout_test.go +0 -55
- package/plugins/extract-sql/lineage.go +0 -107
- package/plugins/extract-sql/main.go +0 -141
- package/plugins/extract-sql/maps.go +0 -564
- package/plugins/extract-sql/maps_java.go +0 -117
- package/plugins/extract-sql/maps_rust.go +0 -333
- package/plugins/extract-sql/maps_rust_test.go +0 -70
- package/plugins/extract-sql/maps_test.go +0 -204
- package/plugins/extract-sql/maps_ts.go +0 -398
- package/plugins/extract-sql/maps_ts_test.go +0 -136
- package/plugins/extract-sql/projection.go +0 -70
- package/plugins/extract-sql/projection_test.go +0 -50
- package/plugins/extract-sql/store.go +0 -420
- package/plugins/extract-sql/store_test.go +0 -233
- package/plugins/extract-sql/view.go +0 -295
- package/plugins/extract-watermill/describe.go +0 -19
- package/plugins/extract-watermill/describe_test.go +0 -11
- package/plugins/extract-watermill/extract.go +0 -1228
- package/plugins/extract-watermill/extract_test.go +0 -234
- package/plugins/extract-watermill/main.go +0 -41
- package/plugins/extract-wsdl/describe.go +0 -19
- package/plugins/extract-wsdl/describe_test.go +0 -11
- package/plugins/extract-wsdl/extract.go +0 -148
- package/plugins/extract-wsdl/extract_test.go +0 -45
- package/plugins/extract-wsdl/main.go +0 -53
- package/plugins/fetch-bsr/auth.go +0 -114
- package/plugins/fetch-bsr/bsr.go +0 -240
- package/plugins/fetch-bsr/cache.go +0 -66
- package/plugins/fetch-bsr/describe.go +0 -19
- package/plugins/fetch-bsr/fetch.go +0 -192
- package/plugins/fetch-bsr/fetch_test.go +0 -465
- package/plugins/fetch-bsr/lock.go +0 -71
- package/plugins/fetch-bsr/main.go +0 -117
- package/plugins/fetch-csr/auth.go +0 -95
- package/plugins/fetch-csr/cache.go +0 -69
- package/plugins/fetch-csr/describe.go +0 -19
- package/plugins/fetch-csr/describe_test.go +0 -11
- package/plugins/fetch-csr/fetch.go +0 -254
- package/plugins/fetch-csr/fetch_test.go +0 -494
- package/plugins/fetch-csr/lock.go +0 -95
- package/plugins/fetch-csr/main.go +0 -122
- package/plugins/fetch-csr/registry.go +0 -209
- package/plugins/fetch-git/cache.go +0 -65
- package/plugins/fetch-git/describe.go +0 -19
- package/plugins/fetch-git/describe_test.go +0 -11
- package/plugins/fetch-git/fetch.go +0 -181
- package/plugins/fetch-git/fetch_test.go +0 -332
- package/plugins/fetch-git/git.go +0 -169
- package/plugins/fetch-git/lock.go +0 -72
- package/plugins/fetch-git/main.go +0 -127
- package/plugins/fetch-git/offline.go +0 -39
- package/plugins/fetch-git/pin.go +0 -90
- package/plugins/fetch-git/pin_test.go +0 -74
- package/plugins/gen-backstage/describe.go +0 -17
- package/plugins/gen-backstage/main.go +0 -22
- package/plugins/gen-backstage/plugin.go +0 -473
- package/plugins/gen-backstage/plugin_test.go +0 -145
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +0 -162
- package/plugins/gen-markdown/aggregate.go +0 -362
- package/plugins/gen-markdown/canonical.go +0 -93
- package/plugins/gen-markdown/context.go +0 -90
- package/plugins/gen-markdown/coverage_test.go +0 -89
- package/plugins/gen-markdown/describe.go +0 -19
- package/plugins/gen-markdown/describe_test.go +0 -11
- package/plugins/gen-markdown/external.go +0 -71
- package/plugins/gen-markdown/flow.go +0 -277
- package/plugins/gen-markdown/glossary.go +0 -76
- package/plugins/gen-markdown/glossary_test.go +0 -148
- package/plugins/gen-markdown/llms.go +0 -302
- package/plugins/gen-markdown/main.go +0 -22
- package/plugins/gen-markdown/markdown.go +0 -254
- package/plugins/gen-markdown/markdown_test.go +0 -100
- package/plugins/gen-markdown/module.go +0 -107
- package/plugins/gen-markdown/plugin.go +0 -39
- package/plugins/gen-markdown/quality_test.go +0 -176
- package/plugins/gen-markdown/redis_test.go +0 -38
- package/plugins/gen-markdown/render.go +0 -344
- package/plugins/gen-markdown/render_test.go +0 -186
- package/plugins/gen-markdown/service.go +0 -456
- package/plugins/gen-markdown/source.go +0 -177
- package/plugins/gen-markdown/store.go +0 -201
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +0 -17
- package/plugins/gen-mermaid/main.go +0 -22
- package/plugins/gen-mermaid/plugin.go +0 -104
- package/plugins/gen-mermaid/plugin_test.go +0 -33
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +0 -261
- package/plugins/openapi/ids_test.go +0 -98
- package/plugins/verify-codeowners/describe.go +0 -19
- package/plugins/verify-codeowners/describe_test.go +0 -11
- package/plugins/verify-codeowners/main.go +0 -75
- package/plugins/verify-codeowners/match.go +0 -85
- package/plugins/verify-codeowners/match_test.go +0 -47
- package/plugins/verify-codeowners/owners.go +0 -164
- package/plugins/verify-codeowners/owners_test.go +0 -225
- package/plugins/verify-codeowners/parse.go +0 -90
- package/plugins/verify-codeowners/parse_test.go +0 -62
- package/plugins/verify-otel/describe.go +0 -19
- package/plugins/verify-otel/describe_test.go +0 -11
- package/plugins/verify-otel/main.go +0 -53
- package/plugins/verify-otel/match.go +0 -336
- package/plugins/verify-otel/otlp.go +0 -200
- package/plugins/verify-otel/verify.go +0 -734
- package/plugins/verify-otel/verify_test.go +0 -460
- /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
- /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"go/ast"
|
|
5
|
-
|
|
6
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
7
|
-
"github.com/shortlink-org/portolan/internal/goscan"
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
// natsCall is one method of nats.go the reader knows: which way messages go,
|
|
11
|
-
// and where in the arguments the subject is - directly, inside a *nats.Msg,
|
|
12
|
-
// or inside a consumer config.
|
|
13
|
-
type natsCall struct {
|
|
14
|
-
direction catalog.ChannelDirection
|
|
15
|
-
api string
|
|
16
|
-
subject int
|
|
17
|
-
message int
|
|
18
|
-
config int
|
|
19
|
-
queue int
|
|
20
|
-
durable int
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
func core(direction catalog.ChannelDirection, subject, queue int) natsCall {
|
|
24
|
-
return natsCall{direction: direction, api: "NATS", subject: subject, message: -1, config: -1, queue: queue, durable: -1}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
func coreMsg(direction catalog.ChannelDirection, message int) natsCall {
|
|
28
|
-
return natsCall{direction: direction, api: "NATS", subject: -1, message: message, config: -1, queue: -1, durable: -1}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func stream(direction catalog.ChannelDirection, subject, queue, durable int) natsCall {
|
|
32
|
-
return natsCall{direction: direction, api: "JetStream", subject: subject, message: -1, config: -1, queue: queue, durable: durable}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
func streamMsg(message int) natsCall {
|
|
36
|
-
return natsCall{direction: catalog.ChannelSend, api: "JetStream", subject: -1, message: message, config: -1, queue: -1, durable: -1}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
func consumer(config int) natsCall {
|
|
40
|
-
return natsCall{direction: catalog.ChannelReceive, api: "JetStream", subject: -1, message: -1, config: config, queue: -1, durable: -1}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// natsCalls is the surface read, by the type a method is called on. The
|
|
44
|
-
// type is what decides: a service's own bus port has a Subscribe too, and
|
|
45
|
-
// it is not this one.
|
|
46
|
-
var natsCalls = map[string]map[string]natsCall{
|
|
47
|
-
natsPkg + ".Conn": {
|
|
48
|
-
"Subscribe": core(catalog.ChannelReceive, 0, -1),
|
|
49
|
-
"SubscribeSync": core(catalog.ChannelReceive, 0, -1),
|
|
50
|
-
"ChanSubscribe": core(catalog.ChannelReceive, 0, -1),
|
|
51
|
-
"QueueSubscribe": core(catalog.ChannelReceive, 0, 1),
|
|
52
|
-
"QueueSubscribeSync": core(catalog.ChannelReceive, 0, 1),
|
|
53
|
-
"ChanQueueSubscribe": core(catalog.ChannelReceive, 0, 1),
|
|
54
|
-
"Publish": core(catalog.ChannelSend, 0, -1),
|
|
55
|
-
"Request": core(catalog.ChannelSend, 0, -1),
|
|
56
|
-
"PublishMsg": coreMsg(catalog.ChannelSend, 0),
|
|
57
|
-
"RequestMsg": coreMsg(catalog.ChannelSend, 0),
|
|
58
|
-
},
|
|
59
|
-
natsPkg + ".JetStreamContext": {
|
|
60
|
-
"Subscribe": stream(catalog.ChannelReceive, 0, -1, -1),
|
|
61
|
-
"SubscribeSync": stream(catalog.ChannelReceive, 0, -1, -1),
|
|
62
|
-
"ChanSubscribe": stream(catalog.ChannelReceive, 0, -1, -1),
|
|
63
|
-
"QueueSubscribe": stream(catalog.ChannelReceive, 0, 1, -1),
|
|
64
|
-
"QueueSubscribeSync": stream(catalog.ChannelReceive, 0, 1, -1),
|
|
65
|
-
"ChanQueueSubscribe": stream(catalog.ChannelReceive, 0, 1, -1),
|
|
66
|
-
"PullSubscribe": stream(catalog.ChannelReceive, 0, -1, 1),
|
|
67
|
-
"Publish": stream(catalog.ChannelSend, 0, -1, -1),
|
|
68
|
-
"PublishAsync": stream(catalog.ChannelSend, 0, -1, -1),
|
|
69
|
-
"PublishMsg": streamMsg(0),
|
|
70
|
-
"PublishMsgAsync": streamMsg(0),
|
|
71
|
-
},
|
|
72
|
-
jsPkg + ".JetStream": {
|
|
73
|
-
"Publish": stream(catalog.ChannelSend, 1, -1, -1),
|
|
74
|
-
"PublishAsync": stream(catalog.ChannelSend, 1, -1, -1),
|
|
75
|
-
"PublishMsg": streamMsg(1),
|
|
76
|
-
"PublishMsgAsync": streamMsg(1),
|
|
77
|
-
"CreateOrUpdateConsumer": consumer(2),
|
|
78
|
-
"CreateConsumer": consumer(2),
|
|
79
|
-
"UpdateConsumer": consumer(2),
|
|
80
|
-
"OrderedConsumer": consumer(2),
|
|
81
|
-
},
|
|
82
|
-
jsPkg + ".Stream": {
|
|
83
|
-
"CreateOrUpdateConsumer": consumer(1),
|
|
84
|
-
"CreateConsumer": consumer(1),
|
|
85
|
-
"UpdateConsumer": consumer(1),
|
|
86
|
-
"OrderedConsumer": consumer(1),
|
|
87
|
-
},
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// site is one nats.go call as found: where, which way, and the expressions
|
|
91
|
-
// that name the subject, the queue group and the durable consumer - still
|
|
92
|
-
// expressions, since what they are worth may be decided by a caller.
|
|
93
|
-
type site struct {
|
|
94
|
-
fn *function
|
|
95
|
-
method string
|
|
96
|
-
direction catalog.ChannelDirection
|
|
97
|
-
api string
|
|
98
|
-
configured bool
|
|
99
|
-
subjects []ast.Expr
|
|
100
|
-
queue ast.Expr
|
|
101
|
-
durable ast.Expr
|
|
102
|
-
at goscan.Source
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// sites is every nats.go call in the tree, in source order.
|
|
106
|
-
func (s *scanner) sites() []site {
|
|
107
|
-
var out []site
|
|
108
|
-
for _, fn := range s.sortedFunctions() {
|
|
109
|
-
ast.Inspect(fn.decl.Body, func(node ast.Node) bool {
|
|
110
|
-
call, ok := node.(*ast.CallExpr)
|
|
111
|
-
if !ok {
|
|
112
|
-
return true
|
|
113
|
-
}
|
|
114
|
-
sel, ok := call.Fun.(*ast.SelectorExpr)
|
|
115
|
-
if !ok {
|
|
116
|
-
return true
|
|
117
|
-
}
|
|
118
|
-
spec, known := natsCalls[s.typeOf(sel.X, fn)][sel.Sel.Name]
|
|
119
|
-
if !known {
|
|
120
|
-
return true
|
|
121
|
-
}
|
|
122
|
-
out = append(out, s.site(fn, call, sel.Sel.Name, spec))
|
|
123
|
-
return true
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
return out
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
func (s *scanner) site(fn *function, call *ast.CallExpr, method string, spec natsCall) site {
|
|
130
|
-
found := site{fn: fn, method: method, direction: spec.direction, api: spec.api, at: s.At(call.Pos())}
|
|
131
|
-
arg := func(index int) ast.Expr {
|
|
132
|
-
if index >= 0 && index < len(call.Args) {
|
|
133
|
-
return call.Args[index]
|
|
134
|
-
}
|
|
135
|
-
return nil
|
|
136
|
-
}
|
|
137
|
-
if subject := arg(spec.subject); subject != nil {
|
|
138
|
-
found.subjects = []ast.Expr{subject}
|
|
139
|
-
}
|
|
140
|
-
if message := arg(spec.message); message != nil {
|
|
141
|
-
if subject := s.messageSubject(message, fn, map[string]bool{}); subject != nil {
|
|
142
|
-
found.subjects = []ast.Expr{subject}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if config := arg(spec.config); config != nil {
|
|
146
|
-
found.configured = true
|
|
147
|
-
found.subjects, found.durable = s.consumerConfig(config, fn)
|
|
148
|
-
}
|
|
149
|
-
found.queue = arg(spec.queue)
|
|
150
|
-
if found.durable == nil {
|
|
151
|
-
found.durable = arg(spec.durable)
|
|
152
|
-
}
|
|
153
|
-
// The legacy API takes the durable name as an option.
|
|
154
|
-
for _, option := range call.Args {
|
|
155
|
-
if optionCall, ok := option.(*ast.CallExpr); ok && s.externalKey(optionCall, fn) == natsPkg+".Durable" && len(optionCall.Args) == 1 {
|
|
156
|
-
found.durable = optionCall.Args[0]
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return found
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// messageSubject is the subject a *nats.Msg was built with: nats.NewMsg(x),
|
|
163
|
-
// &nats.Msg{Subject: x}, or a local that was given one of those.
|
|
164
|
-
func (s *scanner) messageSubject(expr ast.Expr, fn *function, seen map[string]bool) ast.Expr {
|
|
165
|
-
switch value := goscan.Unwrap(expr).(type) {
|
|
166
|
-
case *ast.CallExpr:
|
|
167
|
-
if s.externalKey(value, fn) == natsPkg+".NewMsg" && len(value.Args) == 1 {
|
|
168
|
-
return value.Args[0]
|
|
169
|
-
}
|
|
170
|
-
case *ast.CompositeLit:
|
|
171
|
-
if s.TypeKey(value.Type, fn.file) == natsPkg+".Msg" {
|
|
172
|
-
return field(value, "Subject")
|
|
173
|
-
}
|
|
174
|
-
case *ast.Ident:
|
|
175
|
-
if seen[value.Name] {
|
|
176
|
-
return nil
|
|
177
|
-
}
|
|
178
|
-
seen[value.Name] = true
|
|
179
|
-
if given, ok := s.assignedTo(fn, value.Name); ok && given.index == 0 {
|
|
180
|
-
return s.messageSubject(given.expr, fn, seen)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return nil
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// consumerConfig is what a JetStream consumer config filters on and is
|
|
187
|
-
// called: FilterSubject, or each of FilterSubjects, and Durable.
|
|
188
|
-
func (s *scanner) consumerConfig(expr ast.Expr, fn *function) (subjects []ast.Expr, durable ast.Expr) {
|
|
189
|
-
lit, ok := goscan.Unwrap(expr).(*ast.CompositeLit)
|
|
190
|
-
if !ok {
|
|
191
|
-
if ident, isIdent := goscan.Unwrap(expr).(*ast.Ident); isIdent {
|
|
192
|
-
if given, found := s.assignedTo(fn, ident.Name); found && given.index == 0 {
|
|
193
|
-
lit, ok = goscan.Unwrap(given.expr).(*ast.CompositeLit)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if !ok {
|
|
198
|
-
return nil, nil
|
|
199
|
-
}
|
|
200
|
-
if single := field(lit, "FilterSubject"); single != nil {
|
|
201
|
-
subjects = append(subjects, single)
|
|
202
|
-
}
|
|
203
|
-
if many, isList := goscan.Unwrap(field(lit, "FilterSubjects")).(*ast.CompositeLit); isList {
|
|
204
|
-
subjects = append(subjects, many.Elts...)
|
|
205
|
-
}
|
|
206
|
-
return subjects, field(lit, "Durable")
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// field is the value a composite literal gives a named field, or nil.
|
|
210
|
-
func field(lit *ast.CompositeLit, name string) ast.Expr {
|
|
211
|
-
if lit == nil {
|
|
212
|
-
return nil
|
|
213
|
-
}
|
|
214
|
-
for _, raw := range lit.Elts {
|
|
215
|
-
pair, ok := raw.(*ast.KeyValueExpr)
|
|
216
|
-
if !ok {
|
|
217
|
-
continue
|
|
218
|
-
}
|
|
219
|
-
if key, ok := pair.Key.(*ast.Ident); ok && key.Name == name {
|
|
220
|
-
return pair.Value
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return nil
|
|
224
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
_ "embed"
|
|
5
|
-
|
|
6
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
//go:embed options.schema.json
|
|
10
|
-
var optionsSchema []byte
|
|
11
|
-
|
|
12
|
-
func descriptor() plugin.Descriptor {
|
|
13
|
-
return plugin.Descriptor{
|
|
14
|
-
Name: "extract-graphql",
|
|
15
|
-
Summary: "Reads a GraphQL schema and answers with the interfaces a service provides, one per schema module.",
|
|
16
|
-
Phases: []string{plugin.PhaseExtract},
|
|
17
|
-
Options: optionsSchema,
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
// The schema in, the interfaces a service provides out.
|
|
4
|
-
//
|
|
5
|
-
// A GraphQL schema says three things a catalog wants: what a client may ask
|
|
6
|
-
// for, what comes back, and which of those answers keep arriving. The first
|
|
7
|
-
// two are what every other interface in this estate already carries - methods
|
|
8
|
-
// with a request and a response - and the third is what `Streaming` is for: a
|
|
9
|
-
// subscription is a server stream, and drawing it as a call would be a lie
|
|
10
|
-
// about how the two ends are coupled.
|
|
11
|
-
//
|
|
12
|
-
// What it deliberately does not say is who answers underneath. A BFF's whole
|
|
13
|
-
// job is to fan out to other services, and the schema is silent about that on
|
|
14
|
-
// purpose - the resolvers know, and the extractor that reads the resolvers is
|
|
15
|
-
// the one that reports the calls.
|
|
16
|
-
|
|
17
|
-
import (
|
|
18
|
-
"encoding/json"
|
|
19
|
-
"fmt"
|
|
20
|
-
"io/fs"
|
|
21
|
-
"os"
|
|
22
|
-
"path/filepath"
|
|
23
|
-
"sort"
|
|
24
|
-
"strings"
|
|
25
|
-
|
|
26
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
27
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
// operations are the three root operation types, in the order a reader meets
|
|
31
|
-
// them: ask, change, keep listening.
|
|
32
|
-
var operations = []string{"query", "mutation", "subscription"}
|
|
33
|
-
|
|
34
|
-
func extract(in plugin.Input, opts Options) (plugin.Response, error) {
|
|
35
|
-
b := &plugin.Builder{}
|
|
36
|
-
root := in.Root
|
|
37
|
-
|
|
38
|
-
if opts.Context == "" {
|
|
39
|
-
opts.Context = filepath.Base(root)
|
|
40
|
-
}
|
|
41
|
-
if opts.Service == "" {
|
|
42
|
-
opts.Service = filepath.Base(root)
|
|
43
|
-
}
|
|
44
|
-
api := opts.API
|
|
45
|
-
if api == "" {
|
|
46
|
-
api = opts.Service + ".v1"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
files, err := findSchema(root, opts.Schema)
|
|
50
|
-
if err != nil {
|
|
51
|
-
return plugin.Response{}, err
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
doc := newDocument()
|
|
55
|
-
for _, file := range files {
|
|
56
|
-
source, err := os.ReadFile(file)
|
|
57
|
-
if err != nil {
|
|
58
|
-
return plugin.Response{}, err
|
|
59
|
-
}
|
|
60
|
-
if err := parseInto(doc, string(source), filepath.ToSlash(file), moduleOf(root, file)); err != nil {
|
|
61
|
-
return plugin.Response{}, err
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
provides := rpcServices(doc, api, b)
|
|
66
|
-
if len(provides) == 0 {
|
|
67
|
-
b.Warn(opts.Context+"."+opts.Service, strings.Join(relAll(files), ", ")+" declares no root fields; nothing answers over this schema")
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
fragment := catalog.Catalog{
|
|
71
|
-
GeneratedAt: in.GeneratedAt,
|
|
72
|
-
Commit: in.Commit,
|
|
73
|
-
Contexts: []catalog.BoundedContext{{
|
|
74
|
-
ID: opts.Context,
|
|
75
|
-
Slug: opts.Context,
|
|
76
|
-
// Named by whichever source knows the name. A schema says what the
|
|
77
|
-
// service answers, not what the service is called.
|
|
78
|
-
Services: []catalog.Service{{
|
|
79
|
-
ID: opts.Context + "." + opts.Service,
|
|
80
|
-
Slug: opts.Service,
|
|
81
|
-
Provides: provides,
|
|
82
|
-
Consumes: []catalog.RpcCall{},
|
|
83
|
-
Aggregates: []catalog.Aggregate{},
|
|
84
|
-
}},
|
|
85
|
-
}},
|
|
86
|
-
Defs: map[string]catalog.TypeDef{},
|
|
87
|
-
Flows: []catalog.Flow{},
|
|
88
|
-
Adrs: []catalog.Adr{},
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
92
|
-
if err != nil {
|
|
93
|
-
return plugin.Response{}, err
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
b.File(firstNonEmpty(opts.Out, "graphql.json"), string(encoded)+"\n")
|
|
97
|
-
|
|
98
|
-
return b.Response(), nil
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// rpcServices groups the root fields by the module that declared them.
|
|
102
|
-
//
|
|
103
|
-
// A tag is how an OpenAPI author says "these endpoints belong together"; a
|
|
104
|
-
// schema module - one directory, one `schema.graphql` - is how a GraphQL
|
|
105
|
-
// author says it, and it has the advantage of being a file, so the interface
|
|
106
|
-
// can point at something a reader can open. A schema written in one file has
|
|
107
|
-
// one group, named by the api itself, exactly as an untagged document is.
|
|
108
|
-
func rpcServices(doc *document, api string, b *plugin.Builder) []catalog.RpcService {
|
|
109
|
-
type group struct {
|
|
110
|
-
methods []catalog.RpcMethod
|
|
111
|
-
// extra are the argument shapes invented for this interface. Nobody
|
|
112
|
-
// declared them, so they are carried here rather than looked up.
|
|
113
|
-
extra []catalog.RpcMessage
|
|
114
|
-
queue []string
|
|
115
|
-
seen map[string]bool
|
|
116
|
-
source string
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
groups := map[string]*group{}
|
|
120
|
-
var order []string
|
|
121
|
-
|
|
122
|
-
for _, operation := range operations {
|
|
123
|
-
rootName := doc.root(operation)
|
|
124
|
-
rootType := doc.types[rootName]
|
|
125
|
-
if rootType == nil {
|
|
126
|
-
continue
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
for _, field := range rootType.fields {
|
|
130
|
-
id := interfaceID(api, field.module)
|
|
131
|
-
g, ok := groups[id]
|
|
132
|
-
if !ok {
|
|
133
|
-
g = &group{seen: map[string]bool{}, source: field.file}
|
|
134
|
-
groups[id] = g
|
|
135
|
-
order = append(order, id)
|
|
136
|
-
}
|
|
137
|
-
if g.source != field.file {
|
|
138
|
-
// One module, two files. The interface can only point at one
|
|
139
|
-
// of them, and a reader following it to the wrong half would
|
|
140
|
-
// not know it had.
|
|
141
|
-
b.Warn(id, "root fields come from both "+g.source+" and "+field.file+"; the interface points at the first")
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
method := catalog.RpcMethod{
|
|
145
|
-
Name: methodName(rootName, field.name),
|
|
146
|
-
Doc: field.doc,
|
|
147
|
-
Response: field.typ.name,
|
|
148
|
-
Deprecated: field.deprecated,
|
|
149
|
-
}
|
|
150
|
-
if field.deprecated && field.reason != "" {
|
|
151
|
-
method.Doc = strings.TrimSpace(method.Doc + "\n\nDeprecated: " + field.reason)
|
|
152
|
-
}
|
|
153
|
-
if operation == "subscription" {
|
|
154
|
-
method.Streaming = catalog.StreamingServer
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
request, invented := requestOf(doc, rootName, field)
|
|
158
|
-
method.Request = request
|
|
159
|
-
if invented != nil {
|
|
160
|
-
g.extra = append(g.extra, *invented)
|
|
161
|
-
} else {
|
|
162
|
-
enqueue(doc, g.seen, &g.queue, request)
|
|
163
|
-
}
|
|
164
|
-
for _, arg := range field.args {
|
|
165
|
-
enqueue(doc, g.seen, &g.queue, arg.typ.name)
|
|
166
|
-
}
|
|
167
|
-
enqueue(doc, g.seen, &g.queue, field.typ.name)
|
|
168
|
-
|
|
169
|
-
g.methods = append(g.methods, method)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
sort.Strings(order)
|
|
174
|
-
|
|
175
|
-
out := make([]catalog.RpcService, 0, len(order))
|
|
176
|
-
for _, id := range order {
|
|
177
|
-
g := groups[id]
|
|
178
|
-
out = append(out, catalog.RpcService{
|
|
179
|
-
ID: id,
|
|
180
|
-
Methods: g.methods,
|
|
181
|
-
Source: g.source,
|
|
182
|
-
Messages: append(g.extra, messages(doc, g.seen, g.queue)...),
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return out
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// requestOf names what a field is given.
|
|
190
|
-
//
|
|
191
|
-
// A field whose whole input is one input object is already named by the
|
|
192
|
-
// schema, and inventing a second name for it would be noise. A field with
|
|
193
|
-
// loose arguments has no name for them, so one is made and the arguments
|
|
194
|
-
// become its fields.
|
|
195
|
-
func requestOf(doc *document, rootName string, field fieldDef) (string, *catalog.RpcMessage) {
|
|
196
|
-
if len(field.args) == 0 {
|
|
197
|
-
return "", nil
|
|
198
|
-
}
|
|
199
|
-
if len(field.args) == 1 {
|
|
200
|
-
if t := doc.types[field.args[0].typ.name]; t != nil && t.kind == kindInput && !field.args[0].typ.list {
|
|
201
|
-
return t.name, nil
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
message := catalog.RpcMessage{Name: argsMessage(rootName, field.name)}
|
|
206
|
-
for _, arg := range field.args {
|
|
207
|
-
message.Fields = append(message.Fields, catalog.Field{
|
|
208
|
-
Name: arg.name,
|
|
209
|
-
Type: spell(doc, arg.typ),
|
|
210
|
-
Doc: optional(arg.doc, arg.typ),
|
|
211
|
-
})
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return message.Name, &message
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
func enqueue(doc *document, seen map[string]bool, queue *[]string, name string) {
|
|
218
|
-
t := doc.types[name]
|
|
219
|
-
if t == nil || seen[name] {
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
switch t.kind {
|
|
223
|
-
case kindObject, kindInput, kindInterface, kindUnion:
|
|
224
|
-
seen[name] = true
|
|
225
|
-
*queue = append(*queue, name)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// messages turns every type the group's fields reach into a named shape.
|
|
230
|
-
//
|
|
231
|
-
// The list grows while it is walked - a response names a type, that type names
|
|
232
|
-
// another - for the same reason the OpenAPI extractor's does: a reader who has
|
|
233
|
-
// to follow three links to find out what came back has been given a worse
|
|
234
|
-
// document than the schema.
|
|
235
|
-
func messages(doc *document, seen map[string]bool, queue []string) []catalog.RpcMessage {
|
|
236
|
-
out := []catalog.RpcMessage{}
|
|
237
|
-
|
|
238
|
-
for i := 0; i < len(queue); i++ {
|
|
239
|
-
t := doc.types[queue[i]]
|
|
240
|
-
if t == nil {
|
|
241
|
-
continue
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
message := catalog.RpcMessage{Name: t.name, Fields: []catalog.Field{}}
|
|
245
|
-
for _, field := range t.fields {
|
|
246
|
-
prose := optional(field.doc, field.typ)
|
|
247
|
-
if field.deprecated {
|
|
248
|
-
prose = strings.TrimSpace(prose + "\n\nDeprecated: " + field.reason)
|
|
249
|
-
}
|
|
250
|
-
message.Fields = append(message.Fields, catalog.Field{
|
|
251
|
-
Name: field.name,
|
|
252
|
-
Type: spell(doc, field.typ),
|
|
253
|
-
Doc: prose,
|
|
254
|
-
})
|
|
255
|
-
enqueue(doc, seen, &queue, field.typ.name)
|
|
256
|
-
for _, arg := range field.args {
|
|
257
|
-
enqueue(doc, seen, &queue, arg.typ.name)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
if d := discriminator(doc, t); d != nil {
|
|
261
|
-
message.Discriminator = d
|
|
262
|
-
for _, variant := range d.Variants {
|
|
263
|
-
enqueue(doc, seen, &queue, variant.Message)
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
out = append(out, message)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return out
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// discriminator is how a polymorphic answer says which shape it is. GraphQL
|
|
273
|
-
// has one answer for both of its ways of being polymorphic, and it is the same
|
|
274
|
-
// field a client would select: `__typename`.
|
|
275
|
-
func discriminator(doc *document, t *typeDef) *catalog.RpcDiscriminator {
|
|
276
|
-
var members []string
|
|
277
|
-
switch t.kind {
|
|
278
|
-
case kindUnion:
|
|
279
|
-
members = t.members
|
|
280
|
-
case kindInterface:
|
|
281
|
-
for _, name := range doc.order {
|
|
282
|
-
other := doc.types[name]
|
|
283
|
-
if other.kind == kindObject && contains(other.implements, t.name) {
|
|
284
|
-
members = append(members, other.name)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
if len(members) == 0 {
|
|
289
|
-
return nil
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
variants := make([]catalog.RpcVariant, 0, len(members))
|
|
293
|
-
for _, member := range members {
|
|
294
|
-
variants = append(variants, catalog.RpcVariant{Value: member, Message: member})
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return &catalog.RpcDiscriminator{Property: "__typename", Variants: variants}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// spell renders a field's type. An enum carries its values along with its
|
|
301
|
-
// name, the way an OpenAPI enum does, because a status whose values are three
|
|
302
|
-
// clicks away is a status nobody reads.
|
|
303
|
-
func spell(doc *document, ref typeRef) string {
|
|
304
|
-
spelled := ref.spell()
|
|
305
|
-
if t := doc.types[ref.name]; t != nil && t.kind == kindEnum && len(t.values) > 0 {
|
|
306
|
-
spelled += " enum(" + strings.Join(t.values, " | ") + ")"
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return spelled
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// optional says in prose what `!` says in the schema, because that is where
|
|
313
|
-
// the rest of the catalog says it.
|
|
314
|
-
func optional(doc string, ref typeRef) string {
|
|
315
|
-
if ref.nonNull {
|
|
316
|
-
return doc
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return strings.TrimSpace("Optional. " + doc)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// findSchema locates the documents. Told where they are - a file or a
|
|
323
|
-
// directory - it reads there; otherwise it searches, because a service with
|
|
324
|
-
// its schema in the conventional place should not have to say so.
|
|
325
|
-
func findSchema(root, declared string) ([]string, error) {
|
|
326
|
-
if declared != "" {
|
|
327
|
-
at := filepath.Join(root, filepath.FromSlash(declared))
|
|
328
|
-
info, err := os.Stat(at)
|
|
329
|
-
if err != nil {
|
|
330
|
-
return nil, err
|
|
331
|
-
}
|
|
332
|
-
if !info.IsDir() {
|
|
333
|
-
return []string{at}, nil
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return walkSchema(at)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
found, err := walkSchema(root)
|
|
340
|
-
if err != nil {
|
|
341
|
-
return nil, err
|
|
342
|
-
}
|
|
343
|
-
if len(found) == 0 {
|
|
344
|
-
return nil, fmt.Errorf("no graphql schema under %s, and none named in the options", root)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return found, nil
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
func walkSchema(dir string) ([]string, error) {
|
|
351
|
-
var found []string
|
|
352
|
-
err := filepath.WalkDir(dir, func(p string, entry fs.DirEntry, err error) error {
|
|
353
|
-
if err != nil {
|
|
354
|
-
return err
|
|
355
|
-
}
|
|
356
|
-
if entry.IsDir() {
|
|
357
|
-
switch entry.Name() {
|
|
358
|
-
case "node_modules", "dist", "vendor", ".git":
|
|
359
|
-
return filepath.SkipDir
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return nil
|
|
363
|
-
}
|
|
364
|
-
switch filepath.Ext(entry.Name()) {
|
|
365
|
-
case ".graphql", ".graphqls", ".gql":
|
|
366
|
-
// The generator writes the whole schema out again beside the
|
|
367
|
-
// modules it read - `schema.generated.graphqls` - and reading that
|
|
368
|
-
// copy would give the estate a module nobody wrote, holding every
|
|
369
|
-
// field twice.
|
|
370
|
-
if !strings.Contains(entry.Name(), ".generated.") {
|
|
371
|
-
found = append(found, p)
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return nil
|
|
376
|
-
})
|
|
377
|
-
if err != nil {
|
|
378
|
-
return nil, err
|
|
379
|
-
}
|
|
380
|
-
sort.Strings(found)
|
|
381
|
-
|
|
382
|
-
return found, nil
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// moduleOf is what a file is called for the purpose of grouping. A schema
|
|
386
|
-
// written as `basket/schema.graphql` is the basket module; one written as
|
|
387
|
-
// `basket.graphql` is the same module said another way.
|
|
388
|
-
func moduleOf(root, file string) string {
|
|
389
|
-
rel, err := filepath.Rel(root, file)
|
|
390
|
-
if err != nil {
|
|
391
|
-
rel = file
|
|
392
|
-
}
|
|
393
|
-
name := strings.TrimSuffix(filepath.Base(rel), filepath.Ext(rel))
|
|
394
|
-
if name != "schema" && name != "index" {
|
|
395
|
-
return name
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
parent := filepath.Base(filepath.Dir(rel))
|
|
399
|
-
if parent == "." || parent == string(filepath.Separator) {
|
|
400
|
-
return ""
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return parent
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
func relAll(files []string) []string {
|
|
407
|
-
out := make([]string, 0, len(files))
|
|
408
|
-
for _, file := range files {
|
|
409
|
-
out = append(out, filepath.ToSlash(file))
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return out
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
func contains(values []string, want string) bool {
|
|
416
|
-
for _, value := range values {
|
|
417
|
-
if value == want {
|
|
418
|
-
return true
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
return false
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
func firstNonEmpty(values ...string) string {
|
|
426
|
-
for _, value := range values {
|
|
427
|
-
if strings.TrimSpace(value) != "" {
|
|
428
|
-
return value
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return ""
|
|
433
|
-
}
|