@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,409 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"go/ast"
|
|
5
|
-
"go/token"
|
|
6
|
-
"go/types"
|
|
7
|
-
"strconv"
|
|
8
|
-
"strings"
|
|
9
|
-
|
|
10
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
// A call to another service is read off the generated client, because that is
|
|
14
|
-
// where the fact is written down: protoc-gen-go-grpc emits one constant per
|
|
15
|
-
// rpc holding the full method name, and the client interface beside it;
|
|
16
|
-
// oapi-codegen emits a request builder per operation with its route, beside
|
|
17
|
-
// the document it was generated from (httpclient.go). The use case never
|
|
18
|
-
// names either - it holds a port - so the chain is port → provider → adapter
|
|
19
|
-
// → client, and every link of it is in the tree.
|
|
20
|
-
|
|
21
|
-
// client is one generated client and the calls it carries.
|
|
22
|
-
type client struct {
|
|
23
|
-
// pkg is what the manifest's peers map is keyed by: the proto package,
|
|
24
|
-
// "risk.v1", or the api id, "auth.v1".
|
|
25
|
-
pkg string
|
|
26
|
-
// methods maps a Go method name to the call id: "risk.v1.RiskService/Assess",
|
|
27
|
-
// "auth.v1.Sessions/login".
|
|
28
|
-
methods map[string]string
|
|
29
|
-
// source is the generated file, or the document, the names were read from.
|
|
30
|
-
source string
|
|
31
|
-
// external is the system the document beside an HTTP client is titled
|
|
32
|
-
// after, as an id: what the far end is called when no peers line says it
|
|
33
|
-
// is one of ours. Empty for a gRPC client, whose proto names no system.
|
|
34
|
-
external string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// readClients finds every client a package declares, keyed by the type a port
|
|
38
|
-
// would hold it as: "RiskServiceClient" for gRPC, "ClientWithResponses" and
|
|
39
|
-
// its three siblings for HTTP. The problem, when there is one, says why an
|
|
40
|
-
// HTTP client could not be named.
|
|
41
|
-
func readClients(pkg *pkg) (map[string]client, string) {
|
|
42
|
-
if isHTTPClient(pkg) {
|
|
43
|
-
return readHTTPClient(pkg)
|
|
44
|
-
}
|
|
45
|
-
out := map[string]client{}
|
|
46
|
-
|
|
47
|
-
for _, file := range pkg.files {
|
|
48
|
-
for _, decl := range file.Decls {
|
|
49
|
-
gen, ok := decl.(*ast.GenDecl)
|
|
50
|
-
if !ok || gen.Tok != token.CONST {
|
|
51
|
-
continue
|
|
52
|
-
}
|
|
53
|
-
for _, spec := range gen.Specs {
|
|
54
|
-
value, ok := spec.(*ast.ValueSpec)
|
|
55
|
-
if !ok {
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
for i, name := range value.Names {
|
|
59
|
-
if i >= len(value.Values) {
|
|
60
|
-
continue
|
|
61
|
-
}
|
|
62
|
-
lit, ok := value.Values[i].(*ast.BasicLit)
|
|
63
|
-
if !ok || lit.Kind != token.STRING {
|
|
64
|
-
continue
|
|
65
|
-
}
|
|
66
|
-
full, err := strconv.Unquote(lit.Value)
|
|
67
|
-
if err != nil {
|
|
68
|
-
continue
|
|
69
|
-
}
|
|
70
|
-
goService, service, method, ok := splitFullMethod(name.Name, full)
|
|
71
|
-
if !ok {
|
|
72
|
-
continue
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
key := goService + "Client"
|
|
76
|
-
c, known := out[key]
|
|
77
|
-
if !known {
|
|
78
|
-
c = client{
|
|
79
|
-
pkg: lastDotPrefix(service),
|
|
80
|
-
methods: map[string]string{},
|
|
81
|
-
source: pkg.paths[file],
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
c.methods[method] = service + "/" + method
|
|
85
|
-
out[key] = c
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return out, ""
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// splitFullMethod reads `RiskService_Assess_FullMethodName = "/risk.v1.RiskService/Assess"`
|
|
95
|
-
// into the Go service name, the proto service name and the rpc.
|
|
96
|
-
func splitFullMethod(constName, full string) (goService, service, method string, ok bool) {
|
|
97
|
-
if !strings.HasSuffix(constName, "_FullMethodName") || !strings.HasPrefix(full, "/") {
|
|
98
|
-
return "", "", "", false
|
|
99
|
-
}
|
|
100
|
-
service, method, found := strings.Cut(full[1:], "/")
|
|
101
|
-
if !found || service == "" || method == "" || strings.Contains(method, "/") {
|
|
102
|
-
return "", "", "", false
|
|
103
|
-
}
|
|
104
|
-
goService, ok = strings.CutSuffix(constName, "_"+method+"_FullMethodName")
|
|
105
|
-
if !ok || goService == "" {
|
|
106
|
-
return "", "", "", false
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return goService, service, method, true
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// lastDotPrefix is "risk.v1" for "risk.v1.RiskService".
|
|
113
|
-
func lastDotPrefix(name string) string {
|
|
114
|
-
if i := strings.LastIndex(name, "."); i >= 0 {
|
|
115
|
-
return name[:i]
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return name
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// clientPkg reads the clients of a package by import path, once.
|
|
122
|
-
func (r *flowReader) clientPkg(importPath string) map[string]client {
|
|
123
|
-
if cached, ok := r.clients[importPath]; ok {
|
|
124
|
-
return cached
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
var out map[string]client
|
|
128
|
-
if rel, ok := r.relDir(importPath); ok {
|
|
129
|
-
if pkg, err := parsePkg(r.root, rel); err == nil {
|
|
130
|
-
var problem string
|
|
131
|
-
out, problem = readClients(pkg)
|
|
132
|
-
if problem != "" {
|
|
133
|
-
r.b.Warn(r.opts.svcID, problem)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
r.clients[importPath] = out
|
|
138
|
-
|
|
139
|
-
return out
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// relDir maps an import path inside this module to a directory under root.
|
|
143
|
-
// Anything outside the module is not in the tree, and this reads the tree.
|
|
144
|
-
func (r *flowReader) relDir(importPath string) (string, bool) {
|
|
145
|
-
if r.module == "" {
|
|
146
|
-
return "", false
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return strings.CutPrefix(importPath, r.module+"/")
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// clientOf resolves a type written as `riskpb.RiskServiceClient` to the client
|
|
153
|
-
// it names, or says the type is not a generated client.
|
|
154
|
-
func (r *flowReader) clientOf(declared string, imports map[string]string) (client, bool) {
|
|
155
|
-
selector, name, found := strings.Cut(strings.TrimPrefix(declared, "*"), ".")
|
|
156
|
-
if !found {
|
|
157
|
-
return client{}, false
|
|
158
|
-
}
|
|
159
|
-
client, ok := r.clientPkg(imports[selector])[name]
|
|
160
|
-
|
|
161
|
-
return client, ok
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// rpcHop is one call on a port that turns out to be another service's rpc.
|
|
165
|
-
type rpcHop struct {
|
|
166
|
-
client client
|
|
167
|
-
method string
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// clientCalls says which rpcs a call on a port amounts to: none when the port
|
|
171
|
-
// is not a client, one when the port IS the generated client, and whatever the
|
|
172
|
-
// adapter's method calls when the port is a local interface bound to one in
|
|
173
|
-
// assembly.
|
|
174
|
-
func (r *flowReader) clientCalls(s *scope, declared, method string) []rpcHop {
|
|
175
|
-
if client, ok := r.clientOf(declared, s.imports); ok {
|
|
176
|
-
if _, known := client.methods[method]; !known {
|
|
177
|
-
r.b.Warn(s.key, "port `"+declared+"` is a generated client with no rpc named "+method+"; the call is left out of the flow")
|
|
178
|
-
|
|
179
|
-
return nil
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return []rpcHop{{client: client, method: method}}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
binding, ok := r.adapters[s.key+"."+declared]
|
|
186
|
-
if !ok {
|
|
187
|
-
return nil
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return r.adapterCalls(s.key, declared, binding, method)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// adapterCalls reads the provider that binds a port to a client, follows it to
|
|
194
|
-
// the adapter type it builds, and reads which rpcs the adapter's method runs.
|
|
195
|
-
func (r *flowReader) adapterCalls(key, declared string, binding adapterDecl, method string) []rpcHop {
|
|
196
|
-
var client client
|
|
197
|
-
var clientType string
|
|
198
|
-
for _, param := range params(binding.fn) {
|
|
199
|
-
written := types.ExprString(param)
|
|
200
|
-
if c, ok := r.clientOf(written, binding.imports); ok {
|
|
201
|
-
client, clientType = c, strings.TrimPrefix(written, "*")
|
|
202
|
-
|
|
203
|
-
break
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if clientType == "" {
|
|
207
|
-
// A provider that builds the port out of something other than a
|
|
208
|
-
// client is a binding of a different kind, and not one this can
|
|
209
|
-
// follow to another service.
|
|
210
|
-
return nil
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
adapter, adapterPkg, ok := r.adapterOf(binding)
|
|
214
|
-
if !ok {
|
|
215
|
-
r.b.Warn(key, "port `"+declared+"` is bound to a client of "+client.pkg+" but the provider's result could not be followed to a type; its calls are left out of the flow")
|
|
216
|
-
|
|
217
|
-
return nil
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
fn := adapterPkg.methods(adapter)[method]
|
|
221
|
-
if fn == nil {
|
|
222
|
-
r.b.Warn(key, "port `"+declared+"` is adapted by "+adapter+", which has no method "+method+"; the call is left out of the flow")
|
|
223
|
-
|
|
224
|
-
return nil
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// The field of the adapter that holds the client, by name, so a call on it
|
|
228
|
-
// can be told apart from a call on anything else the adapter holds.
|
|
229
|
-
fields := map[string]string{}
|
|
230
|
-
for _, decl := range adapterPkg.structs() {
|
|
231
|
-
if decl.name == adapter {
|
|
232
|
-
fields = structFields(decl.fields)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
imports := importsOf(adapterPkg)
|
|
236
|
-
recv := receiverIdent(fn)
|
|
237
|
-
|
|
238
|
-
var out []rpcHop
|
|
239
|
-
seen := map[string]bool{}
|
|
240
|
-
for _, site := range callSites(fn) {
|
|
241
|
-
selector, ok := site.call.Fun.(*ast.SelectorExpr)
|
|
242
|
-
if !ok {
|
|
243
|
-
continue
|
|
244
|
-
}
|
|
245
|
-
inner, ok := selector.X.(*ast.SelectorExpr)
|
|
246
|
-
if !ok {
|
|
247
|
-
continue
|
|
248
|
-
}
|
|
249
|
-
owner, ok := inner.X.(*ast.Ident)
|
|
250
|
-
if !ok || owner.Name != recv {
|
|
251
|
-
continue
|
|
252
|
-
}
|
|
253
|
-
held, ok := r.clientOf(fields[inner.Sel.Name], imports)
|
|
254
|
-
if !ok || held.pkg != client.pkg {
|
|
255
|
-
continue
|
|
256
|
-
}
|
|
257
|
-
rpc := selector.Sel.Name
|
|
258
|
-
if _, known := held.methods[rpc]; !known || seen[rpc] {
|
|
259
|
-
continue
|
|
260
|
-
}
|
|
261
|
-
seen[rpc] = true
|
|
262
|
-
out = append(out, rpcHop{client: held, method: rpc})
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if len(out) == 0 {
|
|
266
|
-
r.b.Warn(key, "port `"+declared+"` is adapted by "+adapter+"."+method+", which calls no rpc of the client; the call is left out of the flow")
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return out
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// adapterOf follows a provider's return to the type it builds: a literal of a
|
|
273
|
-
// type declared beside it, or a constructor in another package of this module.
|
|
274
|
-
func (r *flowReader) adapterOf(binding adapterDecl) (string, *pkg, bool) {
|
|
275
|
-
expr := returnedExpr(binding.fn)
|
|
276
|
-
if expr == nil {
|
|
277
|
-
return "", nil, false
|
|
278
|
-
}
|
|
279
|
-
if unary, ok := expr.(*ast.UnaryExpr); ok && unary.Op == token.AND {
|
|
280
|
-
expr = unary.X
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
switch x := expr.(type) {
|
|
284
|
-
case *ast.CompositeLit:
|
|
285
|
-
return r.typeIn(x.Type, binding)
|
|
286
|
-
case *ast.CallExpr:
|
|
287
|
-
selector, ok := x.Fun.(*ast.SelectorExpr)
|
|
288
|
-
if !ok {
|
|
289
|
-
return "", nil, false
|
|
290
|
-
}
|
|
291
|
-
owner, ok := selector.X.(*ast.Ident)
|
|
292
|
-
if !ok {
|
|
293
|
-
return "", nil, false
|
|
294
|
-
}
|
|
295
|
-
rel, ok := r.relDir(binding.imports[owner.Name])
|
|
296
|
-
if !ok {
|
|
297
|
-
return "", nil, false
|
|
298
|
-
}
|
|
299
|
-
pkg, err := parsePkg(r.root, rel)
|
|
300
|
-
if err != nil {
|
|
301
|
-
return "", nil, false
|
|
302
|
-
}
|
|
303
|
-
for _, file := range pkg.files {
|
|
304
|
-
for _, decl := range file.Decls {
|
|
305
|
-
fn, ok := decl.(*ast.FuncDecl)
|
|
306
|
-
if !ok || fn.Recv != nil || fn.Name.Name != selector.Sel.Name {
|
|
307
|
-
continue
|
|
308
|
-
}
|
|
309
|
-
if fn.Type.Results == nil || len(fn.Type.Results.List) == 0 {
|
|
310
|
-
return "", nil, false
|
|
311
|
-
}
|
|
312
|
-
name := strings.TrimPrefix(types.ExprString(fn.Type.Results.List[0].Type), "*")
|
|
313
|
-
if strings.Contains(name, ".") {
|
|
314
|
-
return "", nil, false
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return name, pkg, true
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return "", nil, false
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// typeIn resolves the type of a composite literal: `adapter{}` is in the
|
|
326
|
-
// provider's own package, `risk.Client{}` in an imported one.
|
|
327
|
-
func (r *flowReader) typeIn(expr ast.Expr, binding adapterDecl) (string, *pkg, bool) {
|
|
328
|
-
switch t := expr.(type) {
|
|
329
|
-
case *ast.Ident:
|
|
330
|
-
return t.Name, binding.pkg, true
|
|
331
|
-
case *ast.SelectorExpr:
|
|
332
|
-
owner, ok := t.X.(*ast.Ident)
|
|
333
|
-
if !ok {
|
|
334
|
-
return "", nil, false
|
|
335
|
-
}
|
|
336
|
-
rel, ok := r.relDir(binding.imports[owner.Name])
|
|
337
|
-
if !ok {
|
|
338
|
-
return "", nil, false
|
|
339
|
-
}
|
|
340
|
-
pkg, err := parsePkg(r.root, rel)
|
|
341
|
-
if err != nil {
|
|
342
|
-
return "", nil, false
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return t.Sel.Name, pkg, true
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return "", nil, false
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// returnedExpr is the single value of the function's last return.
|
|
352
|
-
func returnedExpr(fn *ast.FuncDecl) ast.Expr {
|
|
353
|
-
if fn == nil || fn.Body == nil || len(fn.Body.List) == 0 {
|
|
354
|
-
return nil
|
|
355
|
-
}
|
|
356
|
-
ret, ok := fn.Body.List[len(fn.Body.List)-1].(*ast.ReturnStmt)
|
|
357
|
-
if !ok || len(ret.Results) != 1 {
|
|
358
|
-
return nil
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return ret.Results[0]
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// peerLane is the lane a call to another service lands in, and what the step
|
|
365
|
-
// can claim about it. The manifest says which service answers to a proto
|
|
366
|
-
// package; without that line the far end is a package name, which is not a
|
|
367
|
-
// thing the catalog has, and the step says so rather than guessing.
|
|
368
|
-
func (r *flowReader) peerLane(d *flowDraft, client client) (lane, peer string, status catalog.Status) {
|
|
369
|
-
if service, ok := r.opts.peers[client.pkg]; ok {
|
|
370
|
-
context, _, _ := strings.Cut(service, ".")
|
|
371
|
-
|
|
372
|
-
return d.lane(catalog.Participant{ID: service, Kind: catalog.ParticipantService, Context: &context}), service, catalog.StatusDeclared
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// A system outside the estate, with a contract: the call lands on an
|
|
376
|
-
// operation its document declares, so the step is declared, and the lane
|
|
377
|
-
// says what the far end is rather than pretending it is ours. The manifest
|
|
378
|
-
// may name the system; failing that, the document beside the client does,
|
|
379
|
-
// because a service that vendors a document and generates a client from
|
|
380
|
-
// it is calling whatever that document is titled, and nothing in this
|
|
381
|
-
// tree implements it.
|
|
382
|
-
external := r.opts.externals[client.pkg]
|
|
383
|
-
if external == "" && client.external != "" {
|
|
384
|
-
external = client.external
|
|
385
|
-
if !r.warnedPeer[client.pkg] {
|
|
386
|
-
r.b.Warn(r.opts.svcID, "calls "+client.pkg+", which no peers line claims; read as calling "+external+", the system the document beside the client is titled after - add the package under `peers` if it is one of ours")
|
|
387
|
-
r.warnedPeer[client.pkg] = true
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if external != "" {
|
|
391
|
-
return d.lane(catalog.Participant{ID: external, Kind: catalog.ParticipantExternal}), external, catalog.StatusDeclared
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if !r.warnedPeer[client.pkg] {
|
|
395
|
-
r.b.Warn(r.opts.svcID, "calls "+client.pkg+" and the manifest names no peer for that package; add it under `peers` to say which service answers, or under `externals` when the far end is outside the estate, until then the calls are unresolved")
|
|
396
|
-
r.warnedPeer[client.pkg] = true
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// A bare name for the lane, as for the store: a participant that is not
|
|
400
|
-
// a service must not carry a dot, which the diagram model reads as
|
|
401
|
-
// containment. The package stays on the label, and on the call.
|
|
402
|
-
lane = d.lane(catalog.Participant{
|
|
403
|
-
ID: strings.ReplaceAll(client.pkg, ".", "-"),
|
|
404
|
-
Kind: catalog.ParticipantUnknown,
|
|
405
|
-
Label: client.pkg,
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
return lane, client.pkg, catalog.StatusUnresolved
|
|
409
|
-
}
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"os"
|
|
5
|
-
"path/filepath"
|
|
6
|
-
"testing"
|
|
7
|
-
|
|
8
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
9
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
const generatedClient = `package riskpb
|
|
13
|
-
|
|
14
|
-
import "google.golang.org/grpc"
|
|
15
|
-
|
|
16
|
-
const (
|
|
17
|
-
RiskService_Assess_FullMethodName = "/risk.v1.RiskService/Assess"
|
|
18
|
-
RiskService_Report_FullMethodName = "/risk.v1.RiskService/Report"
|
|
19
|
-
unrelated = "/not/a/method/name"
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
type RiskServiceClient interface {
|
|
23
|
-
Assess(ctx context.Context, in *AssessRequest, opts ...grpc.CallOption) (*AssessResponse, error)
|
|
24
|
-
Report(ctx context.Context, in *ReportRequest, opts ...grpc.CallOption) (*ReportResponse, error)
|
|
25
|
-
}
|
|
26
|
-
`
|
|
27
|
-
|
|
28
|
-
// The rpc is read off the constant protoc-gen-go-grpc writes for it, which is
|
|
29
|
-
// the one place in the tree the full name appears as a string.
|
|
30
|
-
func TestClientsAreReadOffTheGeneratedNames(t *testing.T) {
|
|
31
|
-
pkg, err := parseSource("risk_grpc.pb.go", generatedClient)
|
|
32
|
-
if err != nil {
|
|
33
|
-
t.Fatal(err)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
clients, problem := readClients(pkg)
|
|
37
|
-
if problem != "" {
|
|
38
|
-
t.Fatal(problem)
|
|
39
|
-
}
|
|
40
|
-
client, ok := clients["RiskServiceClient"]
|
|
41
|
-
if !ok {
|
|
42
|
-
t.Fatalf("clients = %v", clients)
|
|
43
|
-
}
|
|
44
|
-
if client.pkg != "risk.v1" {
|
|
45
|
-
t.Errorf("pkg = %q", client.pkg)
|
|
46
|
-
}
|
|
47
|
-
if client.methods["Assess"] != "risk.v1.RiskService/Assess" || client.methods["Report"] != "risk.v1.RiskService/Report" {
|
|
48
|
-
t.Errorf("methods = %v", client.methods)
|
|
49
|
-
}
|
|
50
|
-
if len(client.methods) != 2 {
|
|
51
|
-
t.Errorf("methods = %v, want the two rpcs and not the unrelated constant", client.methods)
|
|
52
|
-
}
|
|
53
|
-
if client.source != "risk_grpc.pb.go" {
|
|
54
|
-
t.Errorf("source = %q", client.source)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// The peer is the manifest's to name. With a line for the package the step
|
|
59
|
-
// lands on a service; without one the far end is a package name, which the
|
|
60
|
-
// catalog does not have, and the step says so.
|
|
61
|
-
func TestThePeerIsTheManifestsToName(t *testing.T) {
|
|
62
|
-
client := client{pkg: "risk.v1", methods: map[string]string{"Assess": "risk.v1.RiskService/Assess"}, source: "gen/risk_grpc.pb.go"}
|
|
63
|
-
|
|
64
|
-
named := newFlowReader(t.TempDir(), flowOptions{svcID: "auth.auth", peers: map[string]string{"risk.v1": "shop.risk"}}, &plugin.Builder{})
|
|
65
|
-
d := newDraft()
|
|
66
|
-
named.rpcHop(d, rpcHop{client: client, method: "Assess"}, "usecase.go:12")
|
|
67
|
-
|
|
68
|
-
step := d.steps[0].(*catalog.Step)
|
|
69
|
-
if step.To != "shop.risk" || step.Kind != catalog.StepRPC || step.Ref != "risk.v1.RiskService/Assess" || step.Status != catalog.StatusDeclared {
|
|
70
|
-
t.Errorf("step = %+v", step)
|
|
71
|
-
}
|
|
72
|
-
if lane := d.lanes[0]; lane.Kind != catalog.ParticipantService || lane.Context == nil || *lane.Context != "shop" {
|
|
73
|
-
t.Errorf("lane = %+v", lane)
|
|
74
|
-
}
|
|
75
|
-
calls := named.consumes()
|
|
76
|
-
if len(calls) != 1 || calls[0].Peer != "shop.risk" || calls[0].Source != "gen/risk_grpc.pb.go" || calls[0].Status != catalog.StatusDeclared {
|
|
77
|
-
t.Errorf("consumes = %+v", calls)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
b := &plugin.Builder{}
|
|
81
|
-
unnamed := newFlowReader(t.TempDir(), flowOptions{svcID: "auth.auth"}, b)
|
|
82
|
-
d = newDraft()
|
|
83
|
-
unnamed.rpcHop(d, rpcHop{client: client, method: "Assess"}, "")
|
|
84
|
-
unnamed.rpcHop(d, rpcHop{client: client, method: "Assess"}, "")
|
|
85
|
-
|
|
86
|
-
step = d.steps[0].(*catalog.Step)
|
|
87
|
-
if step.To != "risk-v1" || step.Status != catalog.StatusUnresolved {
|
|
88
|
-
t.Errorf("step = %+v", step)
|
|
89
|
-
}
|
|
90
|
-
if lane := d.lanes[0]; lane.Kind != catalog.ParticipantUnknown || lane.ID != "risk-v1" || lane.Label != "risk.v1" {
|
|
91
|
-
t.Errorf("lane = %+v, want a bare id with the package on the label", lane)
|
|
92
|
-
}
|
|
93
|
-
if calls := unnamed.consumes(); len(calls) != 1 || calls[0].Status != catalog.StatusUnresolved || calls[0].Peer != "risk.v1" {
|
|
94
|
-
t.Errorf("consumes = %+v", calls)
|
|
95
|
-
}
|
|
96
|
-
if n := len(b.Response().Warnings()); n != 1 {
|
|
97
|
-
t.Errorf("diagnostics = %d, want the one warning about the unnamed peer, said once", n)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// A policy on somebody else's event is still a policy. Its trigger has an id
|
|
102
|
-
// this tree cannot form, so the step names the type and resolves to nothing.
|
|
103
|
-
func TestAPolicyOnAForeignEventIsKeptAndUnresolved(t *testing.T) {
|
|
104
|
-
const policy = `package policy
|
|
105
|
-
|
|
106
|
-
import (
|
|
107
|
-
shopevents "github.com/acme/shop/events/v1"
|
|
108
|
-
"github.com/example/auth/internal/domain/user/event"
|
|
109
|
-
"github.com/example/auth/internal/pkg/helper"
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
type OnOrderPlaced struct{}
|
|
113
|
-
|
|
114
|
-
func (p OnOrderPlaced) Handle(ctx context.Context, e any) error {
|
|
115
|
-
placed, ok := e.(shopevents.OrderPlaced)
|
|
116
|
-
_ = placed
|
|
117
|
-
return nil
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
type OnPasswordChanged struct{}
|
|
121
|
-
|
|
122
|
-
func (p OnPasswordChanged) Handle(ctx context.Context, e any) error {
|
|
123
|
-
_, ok := e.(event.PasswordChanged)
|
|
124
|
-
return nil
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
type OnNothing struct{}
|
|
128
|
-
|
|
129
|
-
func (p OnNothing) Handle(ctx context.Context, e any) error {
|
|
130
|
-
_, ok := e.(helper.Thing)
|
|
131
|
-
return nil
|
|
132
|
-
}
|
|
133
|
-
`
|
|
134
|
-
pkg, err := parseSource("policy.go", policy)
|
|
135
|
-
if err != nil {
|
|
136
|
-
t.Fatal(err)
|
|
137
|
-
}
|
|
138
|
-
r := &flowReader{opts: flowOptions{svcID: "auth.auth"}, module: "github.com/example/auth"}
|
|
139
|
-
imports := importsOf(pkg)
|
|
140
|
-
|
|
141
|
-
foreign, ok := r.assertedEvent(pkg.methods("OnOrderPlaced")["Handle"], imports)
|
|
142
|
-
if !ok || foreign.foreign != "github.com/acme/shop/events/v1" || foreign.name != "OrderPlaced" || foreign.id != "" {
|
|
143
|
-
t.Errorf("foreign = %+v %v", foreign, ok)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
own, ok := r.assertedEvent(pkg.methods("OnPasswordChanged")["Handle"], imports)
|
|
147
|
-
if !ok || own.foreign != "" || own.id != "auth.auth.user.PasswordChanged" {
|
|
148
|
-
t.Errorf("own = %+v %v", own, ok)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if ref, ok := r.assertedEvent(pkg.methods("OnNothing")["Handle"], imports); ok {
|
|
152
|
-
t.Errorf("a helper type of this module was read as an event: %+v", ref)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// The whole chain, on a tree: a use case holds a port of its own, assembly
|
|
157
|
-
// fills it with an adapter over a generated client, and the adapter's method
|
|
158
|
-
// is what says which rpc the port's method amounts to.
|
|
159
|
-
func TestAPortBoundToAClientIsARpcHop(t *testing.T) {
|
|
160
|
-
root := t.TempDir()
|
|
161
|
-
write := func(rel, contents string) {
|
|
162
|
-
t.Helper()
|
|
163
|
-
if err := os.MkdirAll(filepath.Dir(filepath.Join(root, rel)), 0o755); err != nil {
|
|
164
|
-
t.Fatal(err)
|
|
165
|
-
}
|
|
166
|
-
if err := os.WriteFile(filepath.Join(root, rel), []byte(contents), 0o644); err != nil {
|
|
167
|
-
t.Fatal(err)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
write("go.mod", "module github.com/example/auth\n\ngo 1.27\n")
|
|
172
|
-
write("internal/infrastructure/risk/gen/riskpb/risk_grpc.pb.go", generatedClient)
|
|
173
|
-
write("internal/infrastructure/risk/client.go", `package risk
|
|
174
|
-
|
|
175
|
-
import (
|
|
176
|
-
"context"
|
|
177
|
-
|
|
178
|
-
"github.com/example/auth/internal/infrastructure/risk/gen/riskpb"
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
type Client struct {
|
|
182
|
-
rpc riskpb.RiskServiceClient
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
func New(rpc riskpb.RiskServiceClient) *Client { return &Client{rpc: rpc} }
|
|
186
|
-
|
|
187
|
-
func (c *Client) Verdict(ctx context.Context, userID string) (bool, error) {
|
|
188
|
-
out, err := c.rpc.Assess(ctx, &riskpb.AssessRequest{UserId: userID})
|
|
189
|
-
if err != nil {
|
|
190
|
-
return false, err
|
|
191
|
-
}
|
|
192
|
-
return out.Block, nil
|
|
193
|
-
}
|
|
194
|
-
`)
|
|
195
|
-
write("internal/di/provider/risk.go", `package provider
|
|
196
|
-
|
|
197
|
-
import (
|
|
198
|
-
"github.com/example/auth/internal/application/session/usecases/login"
|
|
199
|
-
"github.com/example/auth/internal/infrastructure/risk"
|
|
200
|
-
"github.com/example/auth/internal/infrastructure/risk/gen/riskpb"
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
func ProvideRisk(rpc riskpb.RiskServiceClient) login.Risk {
|
|
204
|
-
return risk.New(rpc)
|
|
205
|
-
}
|
|
206
|
-
`)
|
|
207
|
-
write("internal/application/session/usecases/login/usecase.go", `package login
|
|
208
|
-
|
|
209
|
-
import (
|
|
210
|
-
"context"
|
|
211
|
-
|
|
212
|
-
"github.com/example/auth/internal/domain/session"
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
type Risk interface {
|
|
216
|
-
Verdict(ctx context.Context, userID string) (bool, error)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
type UseCase struct {
|
|
220
|
-
repo session.Repository
|
|
221
|
-
risk Risk
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
|
|
225
|
-
block, err := uc.risk.Verdict(ctx, in.UserID)
|
|
226
|
-
if err != nil {
|
|
227
|
-
return err
|
|
228
|
-
}
|
|
229
|
-
if block {
|
|
230
|
-
if err := uc.repo.Save(ctx, nil); err != nil {
|
|
231
|
-
return err
|
|
232
|
-
}
|
|
233
|
-
return ErrBlocked
|
|
234
|
-
}
|
|
235
|
-
return uc.repo.Save(ctx, nil)
|
|
236
|
-
}
|
|
237
|
-
`)
|
|
238
|
-
|
|
239
|
-
b := &plugin.Builder{}
|
|
240
|
-
r := newFlowReader(root, flowOptions{context: "auth", svcID: "auth.auth", service: "auth", store: "pg", peers: map[string]string{"risk.v1": "shop.risk"}}, b)
|
|
241
|
-
d := newDraft()
|
|
242
|
-
d.lane(r.serviceLane())
|
|
243
|
-
r.walkUseCase(d, "session/login", 0)
|
|
244
|
-
|
|
245
|
-
if got := dump(d.steps); got != "s1:Assess alt3{s2:Save } s4:Save " {
|
|
246
|
-
t.Fatalf("nodes = %q", got)
|
|
247
|
-
}
|
|
248
|
-
rpc := d.steps[0].(*catalog.Step)
|
|
249
|
-
if rpc.Kind != catalog.StepRPC || rpc.To != "shop.risk" || rpc.Ref != "risk.v1.RiskService/Assess" || rpc.Status != catalog.StatusDeclared {
|
|
250
|
-
t.Errorf("rpc step = %+v", rpc)
|
|
251
|
-
}
|
|
252
|
-
if rpc.Line != "internal/application/session/usecases/login/usecase.go:19" && filepath.Base(rpc.Line) == "" {
|
|
253
|
-
t.Errorf("line = %q", rpc.Line)
|
|
254
|
-
}
|
|
255
|
-
alt := d.steps[1].(*catalog.Alt)
|
|
256
|
-
if alt.Branches[0].Title != "block" || !alt.Branches[0].Terminal {
|
|
257
|
-
t.Errorf("arm = %+v", alt.Branches[0])
|
|
258
|
-
}
|
|
259
|
-
calls := r.consumes()
|
|
260
|
-
if len(calls) != 1 || calls[0].ID != "risk.v1.RiskService/Assess" || calls[0].Peer != "shop.risk" {
|
|
261
|
-
t.Errorf("consumes = %+v", calls)
|
|
262
|
-
}
|
|
263
|
-
if diags := b.Response().Warnings(); len(diags) != 0 {
|
|
264
|
-
t.Errorf("diagnostics = %+v", diags)
|
|
265
|
-
}
|
|
266
|
-
}
|
|
@@ -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-go",
|
|
15
|
-
Summary: "Reads a Go service with go/parser alone and answers with its domain: aggregates, events, use cases and the flows they imply.",
|
|
16
|
-
Phases: []string{plugin.PhaseExtract},
|
|
17
|
-
Options: optionsSchema,
|
|
18
|
-
}
|
|
19
|
-
}
|