@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,234 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"os"
|
|
6
|
-
"path/filepath"
|
|
7
|
-
"strings"
|
|
8
|
-
"testing"
|
|
9
|
-
|
|
10
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
func write(t *testing.T, root, name, contents string) {
|
|
15
|
-
t.Helper()
|
|
16
|
-
path := filepath.Join(root, name)
|
|
17
|
-
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
18
|
-
t.Fatal(err)
|
|
19
|
-
}
|
|
20
|
-
if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
|
|
21
|
-
t.Fatal(err)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
func extracted(t *testing.T, root string) (catalog.Catalog, plugin.Response) {
|
|
26
|
-
t.Helper()
|
|
27
|
-
resp, err := extract(plugin.Input{Root: root, Commit: "abc", GeneratedAt: "2026-01-01T00:00:00Z"}, Options{Context: "sales", Service: "mailer"})
|
|
28
|
-
if err != nil {
|
|
29
|
-
t.Fatal(err)
|
|
30
|
-
}
|
|
31
|
-
var out catalog.Catalog
|
|
32
|
-
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
|
|
33
|
-
t.Fatal(err)
|
|
34
|
-
}
|
|
35
|
-
return out, resp
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
func TestExtractsConfiguredHandlerAndHelperPublications(t *testing.T) {
|
|
39
|
-
root := t.TempDir()
|
|
40
|
-
write(t, root, "go.mod", "module example.com/mailer\n")
|
|
41
|
-
write(t, root, "config/config.go", `package config
|
|
42
|
-
type Bus struct {
|
|
43
|
-
Input string `+"`envconfig:\"MAIL_INPUT_TOPIC\" default:\"mail.input\"`"+`
|
|
44
|
-
Output string `+"`envconfig:\"MAIL_OUTPUT_TOPIC\" default:\"mail.output\"`"+`
|
|
45
|
-
Errors string `+"`envconfig:\"MAIL_ERROR_TOPIC\" default:\"mail.error\"`"+`
|
|
46
|
-
}
|
|
47
|
-
`)
|
|
48
|
-
write(t, root, "api/messages.go", `package api
|
|
49
|
-
type Input struct {
|
|
50
|
-
ID string `+"`json:\"id\"`"+`
|
|
51
|
-
Valid bool `+"`json:\"valid\"`"+`
|
|
52
|
-
}
|
|
53
|
-
type Output struct { Accepted bool `+"`json:\"accepted\"`"+` }
|
|
54
|
-
type Failure struct { Reason string `+"`json:\"reason\"`"+` }
|
|
55
|
-
`)
|
|
56
|
-
write(t, root, "app/router.go", `package app
|
|
57
|
-
import (
|
|
58
|
-
"encoding/json"
|
|
59
|
-
"github.com/ThreeDotsLabs/watermill"
|
|
60
|
-
"github.com/ThreeDotsLabs/watermill/message"
|
|
61
|
-
"example.com/mailer/api"
|
|
62
|
-
"example.com/mailer/config"
|
|
63
|
-
)
|
|
64
|
-
const ConsumerGroup = "mail-workers"
|
|
65
|
-
func NewSubscriber(config.Bus, string) (message.Subscriber, error) { return nil, nil }
|
|
66
|
-
func publishFailure(pub message.Publisher, topic string) error {
|
|
67
|
-
value := api.Failure{Reason: "bad"}
|
|
68
|
-
payload, _ := json.Marshal(value)
|
|
69
|
-
msg := message.NewMessage(watermill.NewUUID(), payload)
|
|
70
|
-
return pub.Publish(topic, msg)
|
|
71
|
-
}
|
|
72
|
-
func Register(r *message.Router, cfg config.Bus, pub message.Publisher) {
|
|
73
|
-
sub, _ := NewSubscriber(cfg, ConsumerGroup)
|
|
74
|
-
handlerName := "mail_router"
|
|
75
|
-
r.AddHandler(handlerName, cfg.Input, sub, "", nil, func(msg *message.Message) ([]*message.Message, error) {
|
|
76
|
-
var input api.Input
|
|
77
|
-
_ = json.Unmarshal(msg.Payload, &input)
|
|
78
|
-
if !input.Valid {
|
|
79
|
-
_ = publishFailure(pub, cfg.Errors)
|
|
80
|
-
return nil, nil
|
|
81
|
-
}
|
|
82
|
-
value := api.Output{Accepted: true}
|
|
83
|
-
payload, _ := json.Marshal(value)
|
|
84
|
-
out := message.NewMessage(watermill.NewUUID(), payload)
|
|
85
|
-
_ = pub.Publish(cfg.Output, out)
|
|
86
|
-
return nil, nil
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
`)
|
|
90
|
-
|
|
91
|
-
out, _ := extracted(t, root)
|
|
92
|
-
service := out.Contexts[0].Services[0]
|
|
93
|
-
if len(service.Channels) != 3 {
|
|
94
|
-
t.Fatalf("channels = %+v", service.Channels)
|
|
95
|
-
}
|
|
96
|
-
byAddress := map[string]catalog.Channel{}
|
|
97
|
-
for _, channel := range service.Channels {
|
|
98
|
-
byAddress[channel.Address] = channel
|
|
99
|
-
}
|
|
100
|
-
if got := byAddress["mail.input"].Messages[0]; got.Direction != catalog.ChannelReceive || got.Name != "Input" || !strings.Contains(got.Doc, "id string") {
|
|
101
|
-
t.Fatalf("input = %+v", got)
|
|
102
|
-
}
|
|
103
|
-
if got := byAddress["mail.output"].Messages[0]; got.Direction != catalog.ChannelSend || got.Name != "Output" {
|
|
104
|
-
t.Fatalf("output = %+v", got)
|
|
105
|
-
}
|
|
106
|
-
if got := byAddress["mail.error"].Messages[0]; got.Name != "Failure" || !strings.Contains(got.Doc, "reason string") {
|
|
107
|
-
t.Fatalf("error = %+v", got)
|
|
108
|
-
}
|
|
109
|
-
if !strings.Contains(byAddress["mail.input"].Doc, "MAIL_INPUT_TOPIC") {
|
|
110
|
-
t.Fatalf("channel doc = %q", byAddress["mail.input"].Doc)
|
|
111
|
-
}
|
|
112
|
-
if len(out.Flows) != 1 {
|
|
113
|
-
t.Fatalf("flows = %+v", out.Flows)
|
|
114
|
-
}
|
|
115
|
-
flow := out.Flows[0]
|
|
116
|
-
if len(flow.Steps) != 2 || !strings.Contains(flow.Steps[0].(*catalog.Step).Note, "mail-workers") {
|
|
117
|
-
t.Fatalf("flow = %+v", flow)
|
|
118
|
-
}
|
|
119
|
-
branches := flow.Steps[1].(*catalog.Alt).Branches
|
|
120
|
-
if len(branches) != 2 || branches[0].Title != "!input.Valid" || branches[1].Title != "not (!input.Valid)" || !branches[0].Terminal {
|
|
121
|
-
t.Fatalf("branches = %+v", branches)
|
|
122
|
-
}
|
|
123
|
-
receive := flow.Steps[0].(*catalog.Step).Handoff
|
|
124
|
-
errorSend := branches[0].Steps[0].(*catalog.Step).Handoff
|
|
125
|
-
successSend := branches[1].Steps[0].(*catalog.Step).Handoff
|
|
126
|
-
if receive == nil || receive.Direction != "receive" || receive.Transport != "watermill" || receive.Channel != "mail.input" {
|
|
127
|
-
t.Fatalf("receive handoff = %+v", receive)
|
|
128
|
-
}
|
|
129
|
-
if errorSend == nil || errorSend.Direction != "send" || errorSend.Channel != "mail.error" || successSend == nil || successSend.Channel != "mail.output" {
|
|
130
|
-
t.Fatalf("publish handoffs = error %+v, success %+v", errorSend, successSend)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
func TestExtractsNamedNoPublisherHandler(t *testing.T) {
|
|
135
|
-
root := t.TempDir()
|
|
136
|
-
write(t, root, "go.mod", "module example.com/consumer\n")
|
|
137
|
-
write(t, root, "consumer.go", `package consumer
|
|
138
|
-
import (
|
|
139
|
-
"encoding/json"
|
|
140
|
-
"github.com/ThreeDotsLabs/watermill/message"
|
|
141
|
-
)
|
|
142
|
-
type Notice struct { Code string `+"`json:\"code\"`"+` }
|
|
143
|
-
func consume(msg *message.Message) error {
|
|
144
|
-
var notice Notice
|
|
145
|
-
return json.Unmarshal(msg.Payload, ¬ice)
|
|
146
|
-
}
|
|
147
|
-
func Register(r *message.Router, sub message.Subscriber) {
|
|
148
|
-
r.AddNoPublisherHandler("notice_handler", "notice.input", sub, consume)
|
|
149
|
-
}
|
|
150
|
-
`)
|
|
151
|
-
|
|
152
|
-
out, _ := extracted(t, root)
|
|
153
|
-
service := out.Contexts[0].Services[0]
|
|
154
|
-
if len(service.Channels) != 1 || service.Channels[0].Messages[0].Name != "Notice" {
|
|
155
|
-
t.Fatalf("channels = %+v", service.Channels)
|
|
156
|
-
}
|
|
157
|
-
if len(out.Flows) != 1 || len(out.Flows[0].Steps) != 1 {
|
|
158
|
-
t.Fatalf("flows = %+v", out.Flows)
|
|
159
|
-
}
|
|
160
|
-
if got := out.Flows[0].Steps[0].(*catalog.Step).ContinuesAt; got != "consume" {
|
|
161
|
-
t.Fatalf("handler continuation = %q", got)
|
|
162
|
-
}
|
|
163
|
-
if got := out.Flows[0].Steps[0].(*catalog.Step).Handoff; got == nil || got.Kind != "message" || got.Channel != "notice.input" || got.Direction != "receive" {
|
|
164
|
-
t.Fatalf("handler handoff = %+v", got)
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
func TestExtractsGenericCQRSProcessors(t *testing.T) {
|
|
169
|
-
root := t.TempDir()
|
|
170
|
-
write(t, root, "go.mod", "module example.com/cqrsapp\n")
|
|
171
|
-
write(t, root, "cqrs.go", `package cqrsapp
|
|
172
|
-
import (
|
|
173
|
-
"context"
|
|
174
|
-
"github.com/ThreeDotsLabs/watermill/components/cqrs"
|
|
175
|
-
"github.com/ThreeDotsLabs/watermill/message"
|
|
176
|
-
)
|
|
177
|
-
type OrderPlaced struct { OrderID string `+"`json:\"order_id\"`"+` }
|
|
178
|
-
type BookOrder struct { CustomerID string `+"`json:\"customer_id\"`"+` }
|
|
179
|
-
func Register(router *message.Router) {
|
|
180
|
-
events, _ := cqrs.NewEventProcessorWithConfig(router, cqrs.EventProcessorConfig{
|
|
181
|
-
GenerateSubscribeTopic: func(cqrs.EventProcessorGenerateSubscribeTopicParams) (string, error) { return "domain.events", nil },
|
|
182
|
-
})
|
|
183
|
-
project := cqrs.NewEventHandler("project_order", func(ctx context.Context, event *OrderPlaced) error { return nil })
|
|
184
|
-
_ = events.AddHandlers(project)
|
|
185
|
-
|
|
186
|
-
commands, _ := cqrs.NewCommandProcessorWithConfig(router, cqrs.CommandProcessorConfig{
|
|
187
|
-
GenerateSubscribeTopic: func(params cqrs.CommandProcessorGenerateSubscribeTopicParams) (string, error) { return params.CommandName, nil },
|
|
188
|
-
})
|
|
189
|
-
book := cqrs.NewCommandHandler("book_order", func(ctx context.Context, command *BookOrder) error { return nil })
|
|
190
|
-
_, _ = commands.AddHandler(book)
|
|
191
|
-
}
|
|
192
|
-
`)
|
|
193
|
-
|
|
194
|
-
out, _ := extracted(t, root)
|
|
195
|
-
service := out.Contexts[0].Services[0]
|
|
196
|
-
if len(service.Channels) != 2 {
|
|
197
|
-
t.Fatalf("channels = %+v", service.Channels)
|
|
198
|
-
}
|
|
199
|
-
byAddress := map[string]catalog.Channel{}
|
|
200
|
-
for _, channel := range service.Channels {
|
|
201
|
-
byAddress[channel.Address] = channel
|
|
202
|
-
}
|
|
203
|
-
if got := byAddress["domain.events"].Messages[0]; got.Name != "OrderPlaced" || got.Direction != catalog.ChannelReceive {
|
|
204
|
-
t.Fatalf("event = %+v", got)
|
|
205
|
-
}
|
|
206
|
-
if got := byAddress["BookOrder"].Messages[0]; got.Name != "BookOrder" || got.Direction != catalog.ChannelReceive {
|
|
207
|
-
t.Fatalf("command = %+v", got)
|
|
208
|
-
}
|
|
209
|
-
if len(out.Flows) != 2 {
|
|
210
|
-
t.Fatalf("flows = %+v", out.Flows)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
func TestBranchesRequireSourceBackedConditions(t *testing.T) {
|
|
215
|
-
unstructured := []publication{
|
|
216
|
-
{topic: topic{address: "one"}, payload: "One"},
|
|
217
|
-
{topic: topic{address: "two"}, payload: "Two"},
|
|
218
|
-
}
|
|
219
|
-
if structuredBranches(unstructured) {
|
|
220
|
-
t.Fatal("two publications without control-flow evidence became an alt")
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
structured := []publication{
|
|
224
|
-
{topic: topic{address: "errors"}, conditions: []string{"ready", "err != nil"}},
|
|
225
|
-
{topic: topic{address: "success"}, conditions: []string{"ready", "not (err != nil)"}},
|
|
226
|
-
}
|
|
227
|
-
if !structuredBranches(structured) {
|
|
228
|
-
t.Fatal("complementary source paths did not become an alt")
|
|
229
|
-
}
|
|
230
|
-
got := branchConditions(structured)
|
|
231
|
-
if got[0] != "err != nil" || got[1] != "not (err != nil)" {
|
|
232
|
-
t.Fatalf("branch conditions = %v", got)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// Package main is portolan-extract-watermill: Watermill router declarations
|
|
2
|
-
// and publications in a Go repository in, channels and source-backed flows out.
|
|
3
|
-
package main
|
|
4
|
-
|
|
5
|
-
import (
|
|
6
|
-
"fmt"
|
|
7
|
-
"io"
|
|
8
|
-
"os"
|
|
9
|
-
"path/filepath"
|
|
10
|
-
|
|
11
|
-
"github.com/shortlink-org/portolan/internal/goscan"
|
|
12
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
type Options struct {
|
|
16
|
-
Context string `json:"context"`
|
|
17
|
-
Service string `json:"service"`
|
|
18
|
-
Out string `json:"out,omitempty"`
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
func main() {
|
|
22
|
-
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
23
|
-
fmt.Fprintln(os.Stderr, "portolan-extract-watermill:", err)
|
|
24
|
-
os.Exit(1)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
func run(stdin io.Reader, stdout io.Writer) error {
|
|
29
|
-
return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
|
|
30
|
-
if req.Input.Root == "" {
|
|
31
|
-
return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
|
|
32
|
-
}
|
|
33
|
-
if opts.Context == "" {
|
|
34
|
-
opts.Context = goscan.Slug(filepath.Base(req.Input.Root))
|
|
35
|
-
}
|
|
36
|
-
if opts.Service == "" {
|
|
37
|
-
opts.Service = goscan.Slug(filepath.Base(req.Input.Root))
|
|
38
|
-
}
|
|
39
|
-
return extract(req.Input, opts)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
@@ -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-wsdl",
|
|
15
|
-
Summary: "Reads WSDL services, SOAP bindings, operations, messages, faults, and local XSD shapes.",
|
|
16
|
-
Phases: []string{plugin.PhaseExtract},
|
|
17
|
-
Options: optionsSchema,
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"fmt"
|
|
6
|
-
"sort"
|
|
7
|
-
"strings"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
"github.com/shortlink-org/portolan/internal/wsdl"
|
|
11
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
func extract(in plugin.Input, opts Options) (plugin.Response, error) {
|
|
15
|
-
if opts.Mode != "" && opts.Mode != "service" && opts.Mode != "external" {
|
|
16
|
-
return plugin.Response{}, fmt.Errorf("mode %q is not service or external", opts.Mode)
|
|
17
|
-
}
|
|
18
|
-
if opts.External != "" && strings.Contains(opts.External, ".") {
|
|
19
|
-
return plugin.Response{}, fmt.Errorf("external %q has a dot in its id", opts.External)
|
|
20
|
-
}
|
|
21
|
-
var result wsdl.Result
|
|
22
|
-
var err error
|
|
23
|
-
if opts.Spec != "" {
|
|
24
|
-
result, err = wsdl.Read(in.Root, opts.Spec)
|
|
25
|
-
} else {
|
|
26
|
-
result, err = wsdl.Discover(in.Root)
|
|
27
|
-
}
|
|
28
|
-
if err != nil {
|
|
29
|
-
return plugin.Response{}, err
|
|
30
|
-
}
|
|
31
|
-
b := &plugin.Builder{}
|
|
32
|
-
for _, warning := range result.Warnings {
|
|
33
|
-
b.Warn(in.Root, warning)
|
|
34
|
-
}
|
|
35
|
-
fragment := catalog.Catalog{
|
|
36
|
-
GeneratedAt: in.GeneratedAt, Commit: in.Commit,
|
|
37
|
-
Contexts: []catalog.BoundedContext{}, Defs: map[string]catalog.TypeDef{},
|
|
38
|
-
Flows: []catalog.Flow{}, Adrs: []catalog.Adr{}, Externals: []catalog.External{},
|
|
39
|
-
}
|
|
40
|
-
apiIDs := wsdl.APIIDs(result.Contracts)
|
|
41
|
-
if opts.Mode == "external" || opts.External != "" {
|
|
42
|
-
fragment.Externals = externalContracts(result.Contracts, apiIDs, opts)
|
|
43
|
-
} else {
|
|
44
|
-
var provides []catalog.RpcService
|
|
45
|
-
for _, contract := range result.Contracts {
|
|
46
|
-
api := apiOf(contract, apiIDs, opts, len(result.Contracts))
|
|
47
|
-
provides = append(provides, servicesOf(contract, api)...)
|
|
48
|
-
}
|
|
49
|
-
sort.Slice(provides, func(i, j int) bool { return provides[i].ID < provides[j].ID })
|
|
50
|
-
fragment.Contexts = []catalog.BoundedContext{{
|
|
51
|
-
ID: opts.Context, Slug: opts.Context,
|
|
52
|
-
Services: []catalog.Service{{
|
|
53
|
-
ID: opts.Context + "." + opts.Service, Slug: opts.Service,
|
|
54
|
-
Provides: provides, Consumes: []catalog.RpcCall{}, Aggregates: []catalog.Aggregate{},
|
|
55
|
-
}},
|
|
56
|
-
}}
|
|
57
|
-
}
|
|
58
|
-
if len(result.Contracts) == 0 {
|
|
59
|
-
b.Warn(in.Root, "no WSDL service or port type was found")
|
|
60
|
-
}
|
|
61
|
-
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
62
|
-
if err != nil {
|
|
63
|
-
return plugin.Response{}, err
|
|
64
|
-
}
|
|
65
|
-
b.File(firstNonEmpty(opts.Out, "wsdl.json"), string(encoded)+"\n")
|
|
66
|
-
return b.Response(), nil
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
func externalContracts(contracts []wsdl.Contract, apiIDs map[string]string, opts Options) []catalog.External {
|
|
70
|
-
byID := map[string]*catalog.External{}
|
|
71
|
-
for _, contract := range contracts {
|
|
72
|
-
api := apiOf(contract, apiIDs, opts, len(contracts))
|
|
73
|
-
id := firstNonEmpty(opts.External, opts.Externals[api], wsdl.ExternalID(contract))
|
|
74
|
-
external := byID[id]
|
|
75
|
-
if external == nil {
|
|
76
|
-
external = &catalog.External{
|
|
77
|
-
ID: id, Slug: id,
|
|
78
|
-
Name: firstNonEmpty(opts.ExternalName, contract.Name),
|
|
79
|
-
Summary: firstNonEmpty(opts.ExternalSummary, contract.Summary),
|
|
80
|
-
URL: opts.ExternalURL, Provides: []catalog.RpcService{},
|
|
81
|
-
}
|
|
82
|
-
byID[id] = external
|
|
83
|
-
}
|
|
84
|
-
if external.URL == "" {
|
|
85
|
-
for _, iface := range contract.Interfaces {
|
|
86
|
-
if iface.Endpoint != "" {
|
|
87
|
-
external.URL = iface.Endpoint
|
|
88
|
-
break
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
external.Provides = append(external.Provides, servicesOf(contract, api)...)
|
|
93
|
-
}
|
|
94
|
-
var out []catalog.External
|
|
95
|
-
for _, external := range byID {
|
|
96
|
-
sort.Slice(external.Provides, func(i, j int) bool { return external.Provides[i].ID < external.Provides[j].ID })
|
|
97
|
-
out = append(out, *external)
|
|
98
|
-
}
|
|
99
|
-
sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
|
|
100
|
-
return out
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
func apiOf(contract wsdl.Contract, apiIDs map[string]string, opts Options, count int) string {
|
|
104
|
-
if opts.API != "" && count == 1 {
|
|
105
|
-
return opts.API
|
|
106
|
-
}
|
|
107
|
-
return apiIDs[wsdl.ContractKey(contract)]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
func servicesOf(contract wsdl.Contract, api string) []catalog.RpcService {
|
|
111
|
-
var out []catalog.RpcService
|
|
112
|
-
for _, iface := range contract.Interfaces {
|
|
113
|
-
methods := make([]catalog.RpcMethod, 0, len(iface.Operations))
|
|
114
|
-
for _, operation := range iface.Operations {
|
|
115
|
-
methods = append(methods, catalog.RpcMethod{
|
|
116
|
-
Name: operation.Name, Doc: operation.Doc,
|
|
117
|
-
Request: operation.Request, Response: operation.Response,
|
|
118
|
-
SOAP: &catalog.SoapRoute{
|
|
119
|
-
Action: operation.Action, Version: iface.Version, Style: iface.Style,
|
|
120
|
-
Endpoint: iface.Endpoint, Binding: iface.Binding,
|
|
121
|
-
Faults: append([]string(nil), operation.Faults...), Headers: append([]string(nil), operation.Headers...),
|
|
122
|
-
},
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
messages := make([]catalog.RpcMessage, 0, len(iface.Messages))
|
|
126
|
-
for _, message := range iface.Messages {
|
|
127
|
-
fields := make([]catalog.Field, 0, len(message.Fields))
|
|
128
|
-
for _, field := range message.Fields {
|
|
129
|
-
fields = append(fields, catalog.Field{Name: field.Name, Type: field.Type, Doc: field.Doc})
|
|
130
|
-
}
|
|
131
|
-
messages = append(messages, catalog.RpcMessage{Name: message.Name, Fields: fields})
|
|
132
|
-
}
|
|
133
|
-
out = append(out, catalog.RpcService{
|
|
134
|
-
ID: wsdl.InterfaceID(api, iface), Source: contract.Source,
|
|
135
|
-
Methods: methods, Messages: messages,
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
return out
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
func firstNonEmpty(values ...string) string {
|
|
142
|
-
for _, value := range values {
|
|
143
|
-
if strings.TrimSpace(value) != "" {
|
|
144
|
-
return strings.TrimSpace(value)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return ""
|
|
148
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"os"
|
|
6
|
-
"path/filepath"
|
|
7
|
-
"testing"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
func TestWSDLBecomesStructuredExternalAPI(t *testing.T) {
|
|
14
|
-
root := t.TempDir()
|
|
15
|
-
spec := `<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="urn:payments" name="Payments" targetNamespace="urn:payments">
|
|
16
|
-
<types><xsd:schema targetNamespace="urn:payments"><xsd:element name="Pay"><xsd:complexType><xsd:sequence><xsd:element name="amount" type="xsd:decimal"/></xsd:sequence></xsd:complexType></xsd:element></xsd:schema></types>
|
|
17
|
-
<message name="PayRequest"><part name="body" element="tns:Pay"/></message>
|
|
18
|
-
<message name="PayResponse"><part name="id" type="xsd:string"/></message>
|
|
19
|
-
<portType name="PaymentsPortType"><operation name="Pay"><input message="tns:PayRequest"/><output message="tns:PayResponse"/></operation></portType>
|
|
20
|
-
<binding name="PaymentsBinding" type="tns:PaymentsPortType"><soap:binding style="document"/><operation name="Pay"><soap:operation soapAction="urn:pay"/></operation></binding>
|
|
21
|
-
<service name="PaymentsService"><port name="PaymentsPort" binding="tns:PaymentsBinding"><soap:address location="https://pay.example/soap"/></port></service>
|
|
22
|
-
</definitions>`
|
|
23
|
-
if err := os.WriteFile(filepath.Join(root, "payments.wsdl"), []byte(spec), 0o644); err != nil {
|
|
24
|
-
t.Fatal(err)
|
|
25
|
-
}
|
|
26
|
-
resp, err := extract(plugin.Input{Root: root, Commit: "abc", GeneratedAt: "2026-01-01T00:00:00Z"}, Options{Mode: "external", Spec: "payments.wsdl"})
|
|
27
|
-
if err != nil {
|
|
28
|
-
t.Fatal(err)
|
|
29
|
-
}
|
|
30
|
-
var got catalog.Catalog
|
|
31
|
-
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &got); err != nil {
|
|
32
|
-
t.Fatal(err)
|
|
33
|
-
}
|
|
34
|
-
if len(got.Externals) != 1 || got.Externals[0].ID != "payments" || got.Externals[0].URL != "https://pay.example/soap" {
|
|
35
|
-
t.Fatalf("externals = %+v", got.Externals)
|
|
36
|
-
}
|
|
37
|
-
provided := got.Externals[0].Provides[0]
|
|
38
|
-
if provided.ID != "paymentsservice.soap.PaymentsPort" || provided.Source != "payments.wsdl" || len(provided.Messages) != 2 {
|
|
39
|
-
t.Fatalf("provided = %+v", provided)
|
|
40
|
-
}
|
|
41
|
-
method := provided.Methods[0]
|
|
42
|
-
if method.Request != "PayRequest" || method.Response != "PayResponse" || method.SOAP == nil || method.SOAP.Action != "urn:pay" || method.SOAP.Version != "1.1" {
|
|
43
|
-
t.Fatalf("method = %+v", method)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// Package main is portolan-extract-wsdl: WSDL contracts and their local XSD
|
|
2
|
-
// graph in, structured SOAP interfaces in the catalog out.
|
|
3
|
-
package main
|
|
4
|
-
|
|
5
|
-
import (
|
|
6
|
-
"fmt"
|
|
7
|
-
"io"
|
|
8
|
-
"os"
|
|
9
|
-
"path/filepath"
|
|
10
|
-
|
|
11
|
-
"github.com/shortlink-org/portolan/internal/wsdl"
|
|
12
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
type Options struct {
|
|
16
|
-
Context string `json:"context"`
|
|
17
|
-
Service string `json:"service"`
|
|
18
|
-
Spec string `json:"spec,omitempty"`
|
|
19
|
-
API string `json:"api,omitempty"`
|
|
20
|
-
|
|
21
|
-
// Mode is service for a contract implemented by this component, external
|
|
22
|
-
// for copies used to call systems outside the estate.
|
|
23
|
-
Mode string `json:"mode,omitempty"`
|
|
24
|
-
External string `json:"external,omitempty"`
|
|
25
|
-
Externals map[string]string `json:"externals,omitempty"`
|
|
26
|
-
|
|
27
|
-
ExternalName string `json:"externalName,omitempty"`
|
|
28
|
-
ExternalSummary string `json:"externalSummary,omitempty"`
|
|
29
|
-
ExternalURL string `json:"externalUrl,omitempty"`
|
|
30
|
-
Out string `json:"out,omitempty"`
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
func main() {
|
|
34
|
-
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
35
|
-
fmt.Fprintln(os.Stderr, "portolan-extract-wsdl:", err)
|
|
36
|
-
os.Exit(1)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func run(stdin io.Reader, stdout io.Writer) error {
|
|
41
|
-
return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
|
|
42
|
-
if req.Input.Root == "" {
|
|
43
|
-
return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
|
|
44
|
-
}
|
|
45
|
-
if opts.Context == "" {
|
|
46
|
-
opts.Context = wsdl.Slug(filepath.Base(req.Input.Root))
|
|
47
|
-
}
|
|
48
|
-
if opts.Service == "" {
|
|
49
|
-
opts.Service = wsdl.Slug(filepath.Base(req.Input.Root))
|
|
50
|
-
}
|
|
51
|
-
return extract(req.Input, opts)
|
|
52
|
-
})
|
|
53
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
// The only file that reads the environment.
|
|
4
|
-
//
|
|
5
|
-
// The plugin protocol says a plugin has "no ambient state to read, no
|
|
6
|
-
// environment". That is about FACTS: nothing about the estate may come from
|
|
7
|
-
// anywhere but the request, or a fragment stops being reproducible from the
|
|
8
|
-
// tree it describes.
|
|
9
|
-
//
|
|
10
|
-
// A credential is not a fact about the estate. It is how the transport is
|
|
11
|
-
// allowed to open a socket, the same way a git credential is - it changes
|
|
12
|
-
// whether the fetch succeeds, never what the fetch says. Isolating it in one
|
|
13
|
-
// file that nothing else calls is what keeps that claim checkable.
|
|
14
|
-
//
|
|
15
|
-
// It is also why this plugin can never be wasm. It needs a socket and a
|
|
16
|
-
// secret, and `process` exists for exactly that trade.
|
|
17
|
-
|
|
18
|
-
import (
|
|
19
|
-
"bufio"
|
|
20
|
-
"os"
|
|
21
|
-
"path/filepath"
|
|
22
|
-
"strings"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// TokenEnv is where `buf registry login` is usually mirrored in CI.
|
|
26
|
-
const TokenEnv = "BUF_TOKEN"
|
|
27
|
-
|
|
28
|
-
// token finds a credential for a registry, or returns none.
|
|
29
|
-
//
|
|
30
|
-
// None is a normal outcome, not a failure: public modules download
|
|
31
|
-
// anonymously, and an estate whose schemas are public never needs a token.
|
|
32
|
-
func token(registry string) string {
|
|
33
|
-
if t := strings.TrimSpace(os.Getenv(TokenEnv)); t != "" {
|
|
34
|
-
return t
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return netrcToken(registry)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// netrcToken reads the password `buf registry login` wrote.
|
|
41
|
-
func netrcToken(registry string) string {
|
|
42
|
-
home, err := os.UserHomeDir()
|
|
43
|
-
if err != nil {
|
|
44
|
-
return ""
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
for _, name := range []string{".netrc", "_netrc"} {
|
|
48
|
-
if t := readNetrc(filepath.Join(home, name), registry); t != "" {
|
|
49
|
-
return t
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return ""
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// readNetrc walks a netrc for the machine asked about.
|
|
57
|
-
//
|
|
58
|
-
// Deliberately small: netrc is a stream of words, and the only entry that
|
|
59
|
-
// matters here is the password belonging to one machine.
|
|
60
|
-
func readNetrc(path, machine string) string {
|
|
61
|
-
file, err := os.Open(path)
|
|
62
|
-
if err != nil {
|
|
63
|
-
return ""
|
|
64
|
-
}
|
|
65
|
-
defer file.Close()
|
|
66
|
-
|
|
67
|
-
words := bufio.NewScanner(file)
|
|
68
|
-
words.Split(bufio.ScanWords)
|
|
69
|
-
|
|
70
|
-
current := ""
|
|
71
|
-
for words.Scan() {
|
|
72
|
-
switch words.Text() {
|
|
73
|
-
case "machine":
|
|
74
|
-
if !words.Scan() {
|
|
75
|
-
return ""
|
|
76
|
-
}
|
|
77
|
-
current = words.Text()
|
|
78
|
-
case "default":
|
|
79
|
-
current = machine
|
|
80
|
-
case "password":
|
|
81
|
-
if !words.Scan() {
|
|
82
|
-
return ""
|
|
83
|
-
}
|
|
84
|
-
if current == machine {
|
|
85
|
-
return words.Text()
|
|
86
|
-
}
|
|
87
|
-
case "login", "account":
|
|
88
|
-
// Read past the value so it cannot be mistaken for a keyword.
|
|
89
|
-
words.Scan()
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return ""
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// OfflineEnv turns the fetch off. Explicit, rather than inferred from a socket
|
|
97
|
-
// timeout: CI sets it in one line, and a build that would otherwise depend on
|
|
98
|
-
// the registry being up cannot silently start doing so again.
|
|
99
|
-
const OfflineEnv = "PORTOLAN_OFFLINE"
|
|
100
|
-
|
|
101
|
-
func offline() bool {
|
|
102
|
-
if strings.TrimSpace(os.Getenv(OfflineEnv)) != "" {
|
|
103
|
-
return true
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// CI verifies what was committed. It never needs to reach the registry, and
|
|
107
|
-
// a fork's pull request has no secret to reach it with.
|
|
108
|
-
switch strings.ToLower(strings.TrimSpace(os.Getenv("CI"))) {
|
|
109
|
-
case "", "0", "false":
|
|
110
|
-
return false
|
|
111
|
-
default:
|
|
112
|
-
return true
|
|
113
|
-
}
|
|
114
|
-
}
|