@shortlink-org/portolan 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +22 -4
- package/scripts/local-api.test.mjs +84 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +6 -2
- 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 +12 -8
- 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,526 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"fmt"
|
|
6
|
-
"os"
|
|
7
|
-
"path/filepath"
|
|
8
|
-
"strings"
|
|
9
|
-
|
|
10
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
-
"github.com/shortlink-org/portolan/plugins/openapi"
|
|
13
|
-
"gopkg.in/yaml.v3"
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
func extract(in plugin.Input, opts Options) (plugin.Response, error) {
|
|
17
|
-
b := &plugin.Builder{}
|
|
18
|
-
root := in.Root
|
|
19
|
-
|
|
20
|
-
// Whose document this is: a service of the estate, or a system outside it
|
|
21
|
-
// whose copy is vendored beside the adapter that calls it. The two are told
|
|
22
|
-
// apart by the manifest and by nothing in the document, because a document
|
|
23
|
-
// does not know which side of the boundary it was read on.
|
|
24
|
-
external := opts.External != ""
|
|
25
|
-
if external && strings.Contains(opts.External, ".") {
|
|
26
|
-
return plugin.Response{}, fmt.Errorf("external %q has a dot in its id; an external sits at the root and is addressed by a bare name", opts.External)
|
|
27
|
-
}
|
|
28
|
-
if !external {
|
|
29
|
-
if opts.Context == "" {
|
|
30
|
-
opts.Context = filepath.Base(root)
|
|
31
|
-
}
|
|
32
|
-
if opts.Service == "" {
|
|
33
|
-
opts.Service = filepath.Base(root)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
owner := opts.Context + "." + opts.Service
|
|
37
|
-
if external {
|
|
38
|
-
owner = opts.External
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
fragment := catalog.Catalog{
|
|
42
|
-
GeneratedAt: in.GeneratedAt,
|
|
43
|
-
Commit: in.Commit,
|
|
44
|
-
Contexts: []catalog.BoundedContext{},
|
|
45
|
-
Defs: map[string]catalog.TypeDef{},
|
|
46
|
-
Flows: []catalog.Flow{},
|
|
47
|
-
Adrs: []catalog.Adr{},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
var provides []catalog.RpcService
|
|
51
|
-
var externals []catalog.External
|
|
52
|
-
if opts.Spec == "" && !external {
|
|
53
|
-
// Nothing named: the tree says which documents it implements and
|
|
54
|
-
// which it calls.
|
|
55
|
-
provides, externals = discover(root, owner, opts, b)
|
|
56
|
-
} else {
|
|
57
|
-
specPath, err := findSpec(root, opts.Spec, b)
|
|
58
|
-
if err != nil {
|
|
59
|
-
return plugin.Response{}, err
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
doc, err := load(specPath)
|
|
63
|
-
if err != nil {
|
|
64
|
-
return plugin.Response{}, fmt.Errorf("%s: %w", specPath, err)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
source := filepath.ToSlash(specPath)
|
|
68
|
-
api := firstNonEmpty(opts.API, apiID(doc))
|
|
69
|
-
|
|
70
|
-
provides = rpcServices(doc, api, source, b)
|
|
71
|
-
if len(provides) == 0 {
|
|
72
|
-
b.Warn(owner, source+" declares no operations")
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if provides == nil {
|
|
76
|
-
provides = []catalog.RpcService{}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if external {
|
|
80
|
-
// Everything the catalog may claim about a system it does not own: what
|
|
81
|
-
// it answers on, and what the manifest says it is called and is for.
|
|
82
|
-
fragment.Externals = []catalog.External{{
|
|
83
|
-
ID: opts.External,
|
|
84
|
-
Slug: opts.External,
|
|
85
|
-
Name: opts.ExternalName,
|
|
86
|
-
Summary: opts.ExternalSummary,
|
|
87
|
-
URL: opts.ExternalURL,
|
|
88
|
-
Provides: provides,
|
|
89
|
-
}}
|
|
90
|
-
} else {
|
|
91
|
-
fragment.Contexts = []catalog.BoundedContext{{
|
|
92
|
-
ID: opts.Context,
|
|
93
|
-
Slug: opts.Context,
|
|
94
|
-
// Named by whichever source knows the name. This one describes what
|
|
95
|
-
// the service answers, not what it is called, and a fragment that
|
|
96
|
-
// filled these in from an API title would be inventing.
|
|
97
|
-
Services: []catalog.Service{{
|
|
98
|
-
ID: opts.Context + "." + opts.Service,
|
|
99
|
-
Slug: opts.Service,
|
|
100
|
-
Provides: provides,
|
|
101
|
-
Consumes: []catalog.RpcCall{},
|
|
102
|
-
Aggregates: []catalog.Aggregate{},
|
|
103
|
-
}},
|
|
104
|
-
}}
|
|
105
|
-
if len(externals) > 0 {
|
|
106
|
-
fragment.Externals = externals
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
111
|
-
if err != nil {
|
|
112
|
-
return plugin.Response{}, err
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
b.File(firstNonEmpty(opts.Out, "api.json"), string(encoded)+"\n")
|
|
116
|
-
|
|
117
|
-
return b.Response(), nil
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// rpcServices turns one OpenAPI document into one provided HTTP API.
|
|
121
|
-
//
|
|
122
|
-
// Tags organise operations inside a document; they do not declare independent
|
|
123
|
-
// contracts. Treating every tag as an interface repeats shared schemas in every
|
|
124
|
-
// tag card and makes one specification look like many APIs.
|
|
125
|
-
func rpcServices(doc *document, api, source string, b *plugin.Builder) []catalog.RpcService {
|
|
126
|
-
methods := []catalog.RpcMethod{}
|
|
127
|
-
schemas := []schemaRef{}
|
|
128
|
-
seen := map[string]bool{}
|
|
129
|
-
visited := map[string]bool{}
|
|
130
|
-
|
|
131
|
-
for _, p := range entries(child(doc.root, "paths")) {
|
|
132
|
-
for _, verb := range verbs {
|
|
133
|
-
operation := child(p.value, verb)
|
|
134
|
-
if operation == nil {
|
|
135
|
-
continue
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
method := text(child(operation, "operationId"))
|
|
139
|
-
if method == "" {
|
|
140
|
-
// Falling back rather than skipping: an operation with no id is
|
|
141
|
-
// still an endpoint, and a reader looking for it wants to find
|
|
142
|
-
// it under something.
|
|
143
|
-
method = strings.ToUpper(verb) + " " + p.key
|
|
144
|
-
b.Warn(api, p.key+" "+strings.ToUpper(verb)+" has no operationId; listed by verb and path")
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// The name, the route, and the shapes on either side. The
|
|
148
|
-
// document names the last two whenever the body is a $ref, which
|
|
149
|
-
// is what lets a flow draw what comes back from a call and not
|
|
150
|
-
// only that one was made.
|
|
151
|
-
request, response := doc.shapes(p.value, operation)
|
|
152
|
-
methods = append(methods, catalog.RpcMethod{
|
|
153
|
-
Name: method,
|
|
154
|
-
Request: request,
|
|
155
|
-
Response: response,
|
|
156
|
-
HTTP: &catalog.HttpRoute{Method: strings.ToUpper(verb), Path: p.key},
|
|
157
|
-
})
|
|
158
|
-
doc.schemaRefs(operation, &schemas, seen, visited)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if len(methods) == 0 {
|
|
163
|
-
return []catalog.RpcService{}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return []catalog.RpcService{{
|
|
167
|
-
ID: openapi.InterfaceID(api, ""),
|
|
168
|
-
Methods: methods,
|
|
169
|
-
Source: source,
|
|
170
|
-
Messages: messages(doc, schemas, seen, visited, b),
|
|
171
|
-
}}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// messages turns every schema the group's operations reach into a named shape.
|
|
175
|
-
//
|
|
176
|
-
// The list grows while it is walked: a request body names a schema, that schema
|
|
177
|
-
// names another, and a reader who has to follow three links to find out what
|
|
178
|
-
// comes back has been given a worse document than the yaml.
|
|
179
|
-
func messages(doc *document, refs []schemaRef, seen, visited map[string]bool, b *plugin.Builder) []catalog.RpcMessage {
|
|
180
|
-
out := []catalog.RpcMessage{}
|
|
181
|
-
for i := 0; i < len(refs); i++ {
|
|
182
|
-
ref := refs[i]
|
|
183
|
-
// Anything this schema refers to joins the queue behind it.
|
|
184
|
-
ref.doc.schemaRefs(ref.node, &refs, seen, visited)
|
|
185
|
-
|
|
186
|
-
out = append(out, catalog.RpcMessage{
|
|
187
|
-
Name: ref.name,
|
|
188
|
-
Fields: schemaFields(ref.doc, ref.node),
|
|
189
|
-
Discriminator: schemaDiscriminator(ref.doc, ref.node),
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return out
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
func schemaDiscriminator(doc *document, node *yaml.Node) *catalog.RpcDiscriminator {
|
|
197
|
-
return discriminatorIn(doc, node, map[string]bool{})
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
func discriminatorIn(doc *document, node *yaml.Node, resolving map[string]bool) *catalog.RpcDiscriminator {
|
|
201
|
-
if node == nil {
|
|
202
|
-
return nil
|
|
203
|
-
}
|
|
204
|
-
if ref := text(child(node, "$ref")); ref != "" {
|
|
205
|
-
if targetDoc, target, pointer, ok := doc.resolve(ref); ok {
|
|
206
|
-
key := targetDoc.path + "#" + pointer
|
|
207
|
-
if !resolving[key] {
|
|
208
|
-
resolving[key] = true
|
|
209
|
-
found := discriminatorIn(targetDoc, target, resolving)
|
|
210
|
-
delete(resolving, key)
|
|
211
|
-
if found != nil {
|
|
212
|
-
return found
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
discriminator := child(node, "discriminator")
|
|
219
|
-
property := text(child(discriminator, "propertyName"))
|
|
220
|
-
if property != "" {
|
|
221
|
-
byMessage := map[string][]string{}
|
|
222
|
-
var mapped []catalog.RpcVariant
|
|
223
|
-
for _, entry := range entries(child(discriminator, "mapping")) {
|
|
224
|
-
if message := schemaMessageName(doc, text(entry.value)); message != "" {
|
|
225
|
-
byMessage[message] = append(byMessage[message], entry.key)
|
|
226
|
-
mapped = append(mapped, catalog.RpcVariant{Value: entry.key, Message: message})
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
var variants []catalog.RpcVariant
|
|
231
|
-
seen := map[string]bool{}
|
|
232
|
-
appendVariant := func(variant catalog.RpcVariant) {
|
|
233
|
-
key := variant.Value + "\x00" + variant.Message
|
|
234
|
-
if !seen[key] {
|
|
235
|
-
variants = append(variants, variant)
|
|
236
|
-
seen[key] = true
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
for _, keyword := range []string{"oneOf", "anyOf"} {
|
|
240
|
-
for _, branch := range itemsOf(child(node, keyword)) {
|
|
241
|
-
message := schemaMessageName(doc, text(child(branch, "$ref")))
|
|
242
|
-
if message == "" {
|
|
243
|
-
continue
|
|
244
|
-
}
|
|
245
|
-
values := byMessage[message]
|
|
246
|
-
if len(values) == 0 {
|
|
247
|
-
values = []string{message}
|
|
248
|
-
}
|
|
249
|
-
for _, value := range values {
|
|
250
|
-
appendVariant(catalog.RpcVariant{Value: value, Message: message})
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
for _, variant := range mapped {
|
|
255
|
-
appendVariant(variant)
|
|
256
|
-
}
|
|
257
|
-
return &catalog.RpcDiscriminator{Property: property, Variants: variants}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
for _, branch := range itemsOf(child(node, "allOf")) {
|
|
261
|
-
if found := discriminatorIn(doc, branch, resolving); found != nil {
|
|
262
|
-
return found
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return nil
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
func schemaMessageName(doc *document, ref string) string {
|
|
269
|
-
if ref == "" {
|
|
270
|
-
return ""
|
|
271
|
-
}
|
|
272
|
-
if _, _, pointer, ok := doc.resolve(ref); ok {
|
|
273
|
-
if name, schema := schemaName(pointer); schema {
|
|
274
|
-
return name
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
if name, ok := refName(ref); ok {
|
|
278
|
-
return name
|
|
279
|
-
}
|
|
280
|
-
return ""
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
func schemaFields(doc *document, node *yaml.Node) []catalog.Field {
|
|
284
|
-
fields, required := schemaShape(doc, node, map[string]bool{})
|
|
285
|
-
for i := range fields {
|
|
286
|
-
if !required[fields[i].Name] {
|
|
287
|
-
fields[i].Doc = strings.TrimSpace("Optional. " + fields[i].Doc)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return fields
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// schemaShape flattens object composition into the field model the catalog
|
|
294
|
-
// has. allOf contributes every required field; oneOf/anyOf contributes the
|
|
295
|
-
// union, with a field required only when every variant requires it.
|
|
296
|
-
func schemaShape(doc *document, node *yaml.Node, resolving map[string]bool) ([]catalog.Field, map[string]bool) {
|
|
297
|
-
fields := []catalog.Field{}
|
|
298
|
-
required := map[string]bool{}
|
|
299
|
-
merge := func(more []catalog.Field, moreRequired map[string]bool, requireMode bool) {
|
|
300
|
-
for _, field := range more {
|
|
301
|
-
at := -1
|
|
302
|
-
for i := range fields {
|
|
303
|
-
if fields[i].Name == field.Name {
|
|
304
|
-
at = i
|
|
305
|
-
break
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if at < 0 {
|
|
309
|
-
fields = append(fields, field)
|
|
310
|
-
} else {
|
|
311
|
-
if fields[at].Type != field.Type && field.Type != "" {
|
|
312
|
-
fields[at].Type = unionTypes(fields[at].Type, field.Type)
|
|
313
|
-
}
|
|
314
|
-
if fields[at].Doc == "" {
|
|
315
|
-
fields[at].Doc = field.Doc
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
if requireMode && moreRequired[field.Name] {
|
|
319
|
-
required[field.Name] = true
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if ref := text(child(node, "$ref")); ref != "" {
|
|
324
|
-
if targetDoc, target, pointer, ok := doc.resolve(ref); ok {
|
|
325
|
-
key := targetDoc.path + "#" + pointer
|
|
326
|
-
if !resolving[key] {
|
|
327
|
-
resolving[key] = true
|
|
328
|
-
more, moreRequired := schemaShape(targetDoc, target, resolving)
|
|
329
|
-
delete(resolving, key)
|
|
330
|
-
merge(more, moreRequired, true)
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
for _, name := range list(child(node, "required")) {
|
|
336
|
-
required[name] = true
|
|
337
|
-
}
|
|
338
|
-
for _, property := range entries(child(node, "properties")) {
|
|
339
|
-
fields = append(fields, catalog.Field{Name: property.key, Type: typeOf(doc, property.value), Doc: text(child(property.value, "description"))})
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
for _, branch := range itemsOf(child(node, "allOf")) {
|
|
343
|
-
more, moreRequired := schemaShape(doc, branch, resolving)
|
|
344
|
-
merge(more, moreRequired, true)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
for _, keyword := range []string{"oneOf", "anyOf"} {
|
|
348
|
-
variants := itemsOf(child(node, keyword))
|
|
349
|
-
if len(variants) == 0 {
|
|
350
|
-
continue
|
|
351
|
-
}
|
|
352
|
-
counts := map[string]int{}
|
|
353
|
-
for _, branch := range variants {
|
|
354
|
-
more, moreRequired := schemaShape(doc, branch, resolving)
|
|
355
|
-
merge(more, moreRequired, false)
|
|
356
|
-
for name := range moreRequired {
|
|
357
|
-
counts[name]++
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
for name, count := range counts {
|
|
361
|
-
if count == len(variants) {
|
|
362
|
-
required[name] = true
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return fields, required
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
func itemsOf(node *yaml.Node) []*yaml.Node {
|
|
370
|
-
if node == nil || node.Kind != yaml.SequenceNode {
|
|
371
|
-
return nil
|
|
372
|
-
}
|
|
373
|
-
return node.Content
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// typeOf renders composed schemas, enums, maps and OpenAPI 3.1 nullable type
|
|
377
|
-
// arrays while keeping the compact spelling used elsewhere in the catalog.
|
|
378
|
-
func typeOf(doc *document, node *yaml.Node) string {
|
|
379
|
-
if ref := text(child(node, "$ref")); ref != "" {
|
|
380
|
-
if _, _, pointer, ok := doc.resolve(ref); ok {
|
|
381
|
-
if name, schema := schemaName(pointer); schema {
|
|
382
|
-
return name
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
if name, ok := refName(ref); ok {
|
|
386
|
-
return name
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return ref
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
for _, keyword := range []string{"oneOf", "anyOf"} {
|
|
393
|
-
var variants []string
|
|
394
|
-
for _, branch := range itemsOf(child(node, keyword)) {
|
|
395
|
-
variants = append(variants, typeOf(doc, branch))
|
|
396
|
-
}
|
|
397
|
-
if len(variants) > 0 {
|
|
398
|
-
return nullableType(strings.Join(unique(variants), " | "), node)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if branches := itemsOf(child(node, "allOf")); len(branches) > 0 {
|
|
402
|
-
var variants []string
|
|
403
|
-
for _, branch := range branches {
|
|
404
|
-
variants = append(variants, typeOf(doc, branch))
|
|
405
|
-
}
|
|
406
|
-
return nullableType(strings.Join(unique(variants), " & "), node)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
kind := text(child(node, "type"))
|
|
410
|
-
if child(node, "type") != nil && child(node, "type").Kind == yaml.SequenceNode {
|
|
411
|
-
kinds := unique(list(child(node, "type")))
|
|
412
|
-
if format := text(child(node, "format")); format != "" {
|
|
413
|
-
for i := range kinds {
|
|
414
|
-
if kinds[i] != "null" {
|
|
415
|
-
kinds[i] += " (" + format + ")"
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
kind = strings.Join(kinds, " | ")
|
|
420
|
-
if values := list(child(node, "enum")); len(values) > 0 {
|
|
421
|
-
kind += " enum(" + strings.Join(values, " | ") + ")"
|
|
422
|
-
}
|
|
423
|
-
return nullableType(kind, node)
|
|
424
|
-
}
|
|
425
|
-
if kind == "array" {
|
|
426
|
-
return nullableType("[]"+typeOf(doc, child(node, "items")), node)
|
|
427
|
-
}
|
|
428
|
-
if kind == "object" {
|
|
429
|
-
additional := child(node, "additionalProperties")
|
|
430
|
-
if additional != nil && (additional.Kind == yaml.MappingNode || text(additional) == "true") {
|
|
431
|
-
value := "any"
|
|
432
|
-
if additional.Kind == yaml.MappingNode {
|
|
433
|
-
value = typeOf(doc, additional)
|
|
434
|
-
}
|
|
435
|
-
return nullableType("map[string]"+value, node)
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if format := text(child(node, "format")); format != "" {
|
|
440
|
-
kind += " (" + format + ")"
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if kind == "" {
|
|
444
|
-
kind = "object"
|
|
445
|
-
}
|
|
446
|
-
if values := list(child(node, "enum")); len(values) > 0 {
|
|
447
|
-
kind += " enum(" + strings.Join(values, " | ") + ")"
|
|
448
|
-
}
|
|
449
|
-
return nullableType(kind, node)
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
func nullableType(kind string, node *yaml.Node) string {
|
|
453
|
-
if text(child(node, "nullable")) == "true" && !strings.Contains(kind, "null") {
|
|
454
|
-
return kind + " | null"
|
|
455
|
-
}
|
|
456
|
-
return kind
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
func unique(values []string) []string {
|
|
460
|
-
seen := map[string]bool{}
|
|
461
|
-
out := make([]string, 0, len(values))
|
|
462
|
-
for _, value := range values {
|
|
463
|
-
if value != "" && !seen[value] {
|
|
464
|
-
seen[value] = true
|
|
465
|
-
out = append(out, value)
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
return out
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
func unionTypes(left, right string) string {
|
|
472
|
-
return strings.Join(unique(append(strings.Split(left, " | "), strings.Split(right, " | ")...)), " | ")
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// apiID is the document's title and major version: `auth` 1.0.0 gives
|
|
476
|
-
// `auth.v1`. Spelled by the package the client-side extractor shares, so a
|
|
477
|
-
// call and the method it lands on are named alike.
|
|
478
|
-
func apiID(doc *document) string {
|
|
479
|
-
info := child(doc.root, "info")
|
|
480
|
-
|
|
481
|
-
return openapi.DocumentAPIID(text(child(info, "x-portolan-api")), text(child(info, "title")), text(child(info, "version")))
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// findSpec locates the document. Told where it is, it looks there; otherwise it
|
|
485
|
-
// searches, and says what it found so a service with two specs does not get
|
|
486
|
-
// documented from whichever one sorted first without anybody noticing.
|
|
487
|
-
func findSpec(root, declared string, b *plugin.Builder) (string, error) {
|
|
488
|
-
if declared != "" {
|
|
489
|
-
return filepath.Join(root, filepath.FromSlash(declared)), nil
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
var found []string
|
|
493
|
-
err := filepath.WalkDir(root, func(p string, entry os.DirEntry, err error) error {
|
|
494
|
-
if err != nil || entry.IsDir() {
|
|
495
|
-
return err
|
|
496
|
-
}
|
|
497
|
-
switch entry.Name() {
|
|
498
|
-
case "openapi.yaml", "openapi.yml", "swagger.yaml", "swagger.yml":
|
|
499
|
-
found = append(found, p)
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return nil
|
|
503
|
-
})
|
|
504
|
-
if err != nil {
|
|
505
|
-
return "", err
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if len(found) == 0 {
|
|
509
|
-
return "", fmt.Errorf("no openapi document under %s, and none named in the options", root)
|
|
510
|
-
}
|
|
511
|
-
if len(found) > 1 {
|
|
512
|
-
b.Warn("", "found "+strings.Join(found, ", ")+"; reading the first and ignoring the rest")
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
return found[0], nil
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
func firstNonEmpty(values ...string) string {
|
|
519
|
-
for _, value := range values {
|
|
520
|
-
if strings.TrimSpace(value) != "" {
|
|
521
|
-
return value
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
return ""
|
|
526
|
-
}
|