@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,174 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"go/ast"
|
|
5
|
-
"go/token"
|
|
6
|
-
"os"
|
|
7
|
-
"path/filepath"
|
|
8
|
-
"strconv"
|
|
9
|
-
"strings"
|
|
10
|
-
|
|
11
|
-
"github.com/shortlink-org/portolan/plugins/openapi"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
// An HTTP client generated by oapi-codegen is read the way a gRPC client is:
|
|
15
|
-
// the tree already says which route each method calls. `New<Op>Request` builds
|
|
16
|
-
// the request, and its body names the path and the verb; the document the
|
|
17
|
-
// client was generated from is vendored beside it, and says which operation
|
|
18
|
-
// answers on that route and which interface it belongs to. Put together, a
|
|
19
|
-
// call on the client is `auth.v1.Sessions/login` - the same id the OpenAPI
|
|
20
|
-
// extractor gives the method on the other side, because both spell it with
|
|
21
|
-
// the same package.
|
|
22
|
-
|
|
23
|
-
// The four names a generated client is held by in a use case or an adapter.
|
|
24
|
-
var httpClientTypes = []string{"Client", "ClientInterface", "ClientWithResponses", "ClientWithResponsesInterface"}
|
|
25
|
-
|
|
26
|
-
// isHTTPClient says whether a package is one oapi-codegen generated a client
|
|
27
|
-
// into: a ClientInterface and at least one request builder.
|
|
28
|
-
func isHTTPClient(p *pkg) bool {
|
|
29
|
-
var iface, builder bool
|
|
30
|
-
for _, file := range p.files {
|
|
31
|
-
for _, decl := range file.Decls {
|
|
32
|
-
switch d := decl.(type) {
|
|
33
|
-
case *ast.GenDecl:
|
|
34
|
-
for _, spec := range d.Specs {
|
|
35
|
-
if ts, ok := spec.(*ast.TypeSpec); ok && ts.Name.Name == "ClientInterface" {
|
|
36
|
-
if _, ok := ts.Type.(*ast.InterfaceType); ok {
|
|
37
|
-
iface = true
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
case *ast.FuncDecl:
|
|
42
|
-
if d.Recv == nil && requestBuilderOp(d.Name.Name) != "" {
|
|
43
|
-
builder = true
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return iface && builder
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// requestBuilderOp reads the operation's Go name off a request builder:
|
|
53
|
-
// NewLoginRequest and NewLoginRequestWithBody are both Login.
|
|
54
|
-
func requestBuilderOp(name string) string {
|
|
55
|
-
rest, ok := strings.CutPrefix(name, "New")
|
|
56
|
-
if !ok {
|
|
57
|
-
return ""
|
|
58
|
-
}
|
|
59
|
-
if op, ok := strings.CutSuffix(rest, "RequestWithBody"); ok {
|
|
60
|
-
return op
|
|
61
|
-
}
|
|
62
|
-
if op, ok := strings.CutSuffix(rest, "Request"); ok {
|
|
63
|
-
return op
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return ""
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// readHTTPClient reads the routes the generated client calls and names them
|
|
70
|
-
// by the vendored document. The problem, when there is one, is a sentence
|
|
71
|
-
// for the reader of the diagnostics: no document beside the client, or a
|
|
72
|
-
// route the document does not declare.
|
|
73
|
-
func readHTTPClient(p *pkg) (map[string]client, string) {
|
|
74
|
-
spec, source, problem := specBeside(p)
|
|
75
|
-
if problem != "" {
|
|
76
|
-
return nil, problem
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
c := client{pkg: spec.API, methods: map[string]string{}, source: source, external: openapi.ExternalID(spec.Title)}
|
|
80
|
-
var unknown []string
|
|
81
|
-
for _, file := range p.files {
|
|
82
|
-
for _, decl := range file.Decls {
|
|
83
|
-
fn, ok := decl.(*ast.FuncDecl)
|
|
84
|
-
if !ok || fn.Recv != nil {
|
|
85
|
-
continue
|
|
86
|
-
}
|
|
87
|
-
op := requestBuilderOp(fn.Name.Name)
|
|
88
|
-
if op == "" {
|
|
89
|
-
continue
|
|
90
|
-
}
|
|
91
|
-
verb, path := routeOf(fn)
|
|
92
|
-
if verb == "" || path == "" {
|
|
93
|
-
continue
|
|
94
|
-
}
|
|
95
|
-
found, ok := spec.Find(verb, path)
|
|
96
|
-
if !ok {
|
|
97
|
-
unknown = append(unknown, verb+" "+path)
|
|
98
|
-
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
id := found.CallID(spec.API)
|
|
102
|
-
// Every way the client offers the operation, to the same call.
|
|
103
|
-
for _, variant := range []string{op, op + "WithBody", op + "WithResponse", op + "WithBodyWithResponse"} {
|
|
104
|
-
c.methods[variant] = id
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
out := map[string]client{}
|
|
110
|
-
for _, name := range httpClientTypes {
|
|
111
|
-
out[name] = c
|
|
112
|
-
}
|
|
113
|
-
if len(unknown) > 0 {
|
|
114
|
-
return out, "the client in " + p.dir + " calls " + strings.Join(unknown, ", ") + ", which the document beside it does not declare; those calls are left out"
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return out, ""
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// specBeside finds the document the client was generated from, in the same
|
|
121
|
-
// directory, and reads it.
|
|
122
|
-
func specBeside(p *pkg) (*openapi.Spec, string, string) {
|
|
123
|
-
for _, name := range []string{"openapi.yaml", "openapi.yml", "swagger.yaml", "swagger.yml"} {
|
|
124
|
-
path := filepath.Join(filepath.FromSlash(p.dir), name)
|
|
125
|
-
if _, err := os.Stat(path); err != nil {
|
|
126
|
-
continue
|
|
127
|
-
}
|
|
128
|
-
spec, err := openapi.Read(path)
|
|
129
|
-
if err != nil {
|
|
130
|
-
return nil, "", err.Error()
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return spec, filepath.ToSlash(path), ""
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return nil, "", "the client in " + p.dir + " has no openapi document beside it, so its calls cannot be named; vendor the document the client was generated from"
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// routeOf reads the verb and path out of a request builder's body:
|
|
140
|
-
//
|
|
141
|
-
// operationPath := fmt.Sprintf("/v1/users/%s", pathParam0)
|
|
142
|
-
// req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil)
|
|
143
|
-
func routeOf(fn *ast.FuncDecl) (verb, path string) {
|
|
144
|
-
ast.Inspect(fn.Body, func(node ast.Node) bool {
|
|
145
|
-
switch n := node.(type) {
|
|
146
|
-
case *ast.AssignStmt:
|
|
147
|
-
if len(n.Lhs) == 1 && len(n.Rhs) == 1 {
|
|
148
|
-
if ident, ok := n.Lhs[0].(*ast.Ident); ok && ident.Name == "operationPath" {
|
|
149
|
-
if call, ok := n.Rhs[0].(*ast.CallExpr); ok && len(call.Args) > 0 {
|
|
150
|
-
if lit, ok := call.Args[0].(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
|
151
|
-
path, _ = strconv.Unquote(lit.Value)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
case *ast.CallExpr:
|
|
157
|
-
if sel, ok := n.Fun.(*ast.SelectorExpr); ok && sel.Sel.Name == "NewRequest" && len(n.Args) > 0 {
|
|
158
|
-
switch arg := n.Args[0].(type) {
|
|
159
|
-
case *ast.SelectorExpr:
|
|
160
|
-
// http.MethodPost
|
|
161
|
-
verb = strings.ToUpper(strings.TrimPrefix(arg.Sel.Name, "Method"))
|
|
162
|
-
case *ast.BasicLit:
|
|
163
|
-
if s, err := strconv.Unquote(arg.Value); err == nil {
|
|
164
|
-
verb = strings.ToUpper(s)
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return true
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
return verb, path
|
|
174
|
-
}
|
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"os"
|
|
5
|
-
"path/filepath"
|
|
6
|
-
"strings"
|
|
7
|
-
"testing"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
// The shape oapi-codegen v2 generates, cut to what the reader looks at: the
|
|
14
|
-
// interface, and the request builders with their route.
|
|
15
|
-
const generatedHTTPClient = `package gen
|
|
16
|
-
|
|
17
|
-
import (
|
|
18
|
-
"context"
|
|
19
|
-
"fmt"
|
|
20
|
-
"io"
|
|
21
|
-
"net/http"
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
type ClientInterface interface {
|
|
25
|
-
LoginWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
|
26
|
-
Login(ctx context.Context, body LoginJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
|
27
|
-
GetUser(ctx context.Context, userId string, reqEditors ...RequestEditorFn) (*http.Response, error)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type ClientWithResponsesInterface interface {
|
|
31
|
-
LoginWithResponse(ctx context.Context, body LoginJSONRequestBody, reqEditors ...RequestEditorFn) (*LoginResponse, error)
|
|
32
|
-
GetUserWithResponse(ctx context.Context, userId string, reqEditors ...RequestEditorFn) (*GetUserResponse, error)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type ClientWithResponses struct{ ClientInterface }
|
|
36
|
-
|
|
37
|
-
func NewLoginRequest(server string, body LoginJSONRequestBody) (*http.Request, error) {
|
|
38
|
-
return NewLoginRequestWithBody(server, "application/json", nil)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
func NewLoginRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
|
|
42
|
-
operationPath := fmt.Sprintf("/v1/sessions")
|
|
43
|
-
req, err := http.NewRequest(http.MethodPost, server+operationPath, body)
|
|
44
|
-
return req, err
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func NewGetUserRequest(server string, userId string) (*http.Request, error) {
|
|
48
|
-
pathParam0 := userId
|
|
49
|
-
operationPath := fmt.Sprintf("/v1/users/%s", pathParam0)
|
|
50
|
-
req, err := http.NewRequest("GET", server+operationPath, nil)
|
|
51
|
-
return req, err
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
func NewPingRequest(server string) (*http.Request, error) {
|
|
55
|
-
operationPath := fmt.Sprintf("/v1/ping")
|
|
56
|
-
req, err := http.NewRequest(http.MethodGet, server+operationPath, nil)
|
|
57
|
-
return req, err
|
|
58
|
-
}
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
const vendoredSpec = `openapi: 3.0.3
|
|
62
|
-
info:
|
|
63
|
-
title: auth
|
|
64
|
-
version: 1.0.0
|
|
65
|
-
paths:
|
|
66
|
-
/v1/sessions:
|
|
67
|
-
post:
|
|
68
|
-
operationId: login
|
|
69
|
-
tags: [sessions]
|
|
70
|
-
/v1/users/{userId}:
|
|
71
|
-
get:
|
|
72
|
-
operationId: getUser
|
|
73
|
-
tags: [users]
|
|
74
|
-
`
|
|
75
|
-
|
|
76
|
-
func writeTree(t *testing.T, root string, files map[string]string) {
|
|
77
|
-
t.Helper()
|
|
78
|
-
for rel, contents := range files {
|
|
79
|
-
if err := os.MkdirAll(filepath.Dir(filepath.Join(root, rel)), 0o755); err != nil {
|
|
80
|
-
t.Fatal(err)
|
|
81
|
-
}
|
|
82
|
-
if err := os.WriteFile(filepath.Join(root, rel), []byte(contents), 0o644); err != nil {
|
|
83
|
-
t.Fatal(err)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// The route is read off the request builder and named by the document
|
|
89
|
-
// beside it, and every way the client offers the operation is the same call.
|
|
90
|
-
func TestAnHTTPClientIsNamedByTheDocumentBesideIt(t *testing.T) {
|
|
91
|
-
root := t.TempDir()
|
|
92
|
-
writeTree(t, root, map[string]string{
|
|
93
|
-
"internal/infrastructure/auth/gen/client.gen.go": generatedHTTPClient,
|
|
94
|
-
"internal/infrastructure/auth/gen/openapi.yaml": vendoredSpec,
|
|
95
|
-
})
|
|
96
|
-
pkg, err := parsePkg(root, "internal/infrastructure/auth/gen")
|
|
97
|
-
if err != nil {
|
|
98
|
-
t.Fatal(err)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
clients, problem := readClients(pkg)
|
|
102
|
-
if !strings.Contains(problem, "GET /v1/ping") {
|
|
103
|
-
t.Errorf("a route the document does not declare should be reported: %q", problem)
|
|
104
|
-
}
|
|
105
|
-
c, ok := clients["ClientWithResponsesInterface"]
|
|
106
|
-
if !ok || c.pkg != "auth.v1" {
|
|
107
|
-
t.Fatalf("clients = %+v", clients)
|
|
108
|
-
}
|
|
109
|
-
for _, name := range httpClientTypes {
|
|
110
|
-
if _, ok := clients[name]; !ok {
|
|
111
|
-
t.Errorf("no client under %s", name)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
want := map[string]string{
|
|
115
|
-
"Login": "auth.v1/login",
|
|
116
|
-
"LoginWithBody": "auth.v1/login",
|
|
117
|
-
"LoginWithResponse": "auth.v1/login",
|
|
118
|
-
"GetUserWithResponse": "auth.v1/getUser",
|
|
119
|
-
"GetUser": "auth.v1/getUser",
|
|
120
|
-
"GetUserWithBodyWithResponse": "auth.v1/getUser",
|
|
121
|
-
}
|
|
122
|
-
for method, id := range want {
|
|
123
|
-
if c.methods[method] != id {
|
|
124
|
-
t.Errorf("%s = %q, want %q", method, c.methods[method], id)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if _, named := c.methods["Ping"]; named {
|
|
128
|
-
t.Error("a route the document does not declare was given a name")
|
|
129
|
-
}
|
|
130
|
-
if !strings.HasSuffix(c.source, "openapi.yaml") {
|
|
131
|
-
t.Errorf("source = %q, want the document", c.source)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Without the document the client cannot be named, and the reader says so
|
|
136
|
-
// rather than inventing an id from the route.
|
|
137
|
-
func TestAnHTTPClientWithoutItsDocumentIsReported(t *testing.T) {
|
|
138
|
-
root := t.TempDir()
|
|
139
|
-
writeTree(t, root, map[string]string{
|
|
140
|
-
"internal/infrastructure/auth/gen/client.gen.go": generatedHTTPClient,
|
|
141
|
-
})
|
|
142
|
-
pkg, err := parsePkg(root, "internal/infrastructure/auth/gen")
|
|
143
|
-
if err != nil {
|
|
144
|
-
t.Fatal(err)
|
|
145
|
-
}
|
|
146
|
-
clients, problem := readClients(pkg)
|
|
147
|
-
if len(clients) != 0 || !strings.Contains(problem, "no openapi document beside it") {
|
|
148
|
-
t.Errorf("clients = %v, problem = %q", clients, problem)
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// The whole chain over HTTP: a use case holds the generated client as a
|
|
153
|
-
// port, and a call on it is an rpc to the service the manifest names for the
|
|
154
|
-
// api, with the call on the service's consumes.
|
|
155
|
-
func TestAUseCaseCallingAnHTTPClientIsAnRpcHop(t *testing.T) {
|
|
156
|
-
root := t.TempDir()
|
|
157
|
-
writeTree(t, root, map[string]string{
|
|
158
|
-
"go.mod": "module github.com/example/gateway\n\ngo 1.27\n",
|
|
159
|
-
"internal/infrastructure/auth/gen/client.gen.go": generatedHTTPClient,
|
|
160
|
-
"internal/infrastructure/auth/gen/openapi.yaml": vendoredSpec,
|
|
161
|
-
"internal/application/session/usecases/whoami/usecase.go": `package whoami
|
|
162
|
-
|
|
163
|
-
import (
|
|
164
|
-
"context"
|
|
165
|
-
|
|
166
|
-
"github.com/example/gateway/internal/infrastructure/auth/gen"
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
type UseCase struct {
|
|
170
|
-
auth gen.ClientWithResponsesInterface
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) (dto.Output, error) {
|
|
174
|
-
resp, err := uc.auth.GetUserWithResponse(ctx, in.UserID)
|
|
175
|
-
if err != nil {
|
|
176
|
-
return dto.Output{}, err
|
|
177
|
-
}
|
|
178
|
-
switch resp.StatusCode() {
|
|
179
|
-
case 200:
|
|
180
|
-
_, err := uc.auth.LoginWithResponse(ctx, gen.LoginJSONRequestBody{})
|
|
181
|
-
return dto.Output{}, err
|
|
182
|
-
case 401, 403:
|
|
183
|
-
return dto.Output{}, ErrRefused
|
|
184
|
-
}
|
|
185
|
-
return dto.Output{}, nil
|
|
186
|
-
}
|
|
187
|
-
`,
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
b := &plugin.Builder{}
|
|
191
|
-
r := newFlowReader(root, flowOptions{context: "edge", svcID: "edge.gateway", service: "gateway", peers: map[string]string{"auth.v1": "auth.auth"}}, b)
|
|
192
|
-
d := newDraft()
|
|
193
|
-
d.lane(r.serviceLane())
|
|
194
|
-
r.walkUseCase(d, "session/whoami", 0)
|
|
195
|
-
|
|
196
|
-
if got := dump(d.steps); got != "s1:getUser alt3{s2:login } " {
|
|
197
|
-
t.Fatalf("nodes = %q", got)
|
|
198
|
-
}
|
|
199
|
-
first := d.steps[0].(*catalog.Step)
|
|
200
|
-
if first.Kind != catalog.StepRPC || first.To != "auth.auth" || first.Ref != "auth.v1/getUser" || first.Status != catalog.StatusDeclared {
|
|
201
|
-
t.Errorf("first = %+v", first)
|
|
202
|
-
}
|
|
203
|
-
alt := d.steps[1].(*catalog.Alt)
|
|
204
|
-
titles := []string{}
|
|
205
|
-
for _, br := range alt.Branches {
|
|
206
|
-
titles = append(titles, br.Title)
|
|
207
|
-
}
|
|
208
|
-
if strings.Join(titles, "|") != "resp.StatusCode() is 200|resp.StatusCode() is 401, 403|otherwise" {
|
|
209
|
-
t.Errorf("titles = %q", titles)
|
|
210
|
-
}
|
|
211
|
-
if !alt.Branches[0].Terminal || !alt.Branches[1].Terminal || alt.Branches[2].Terminal {
|
|
212
|
-
t.Errorf("terminal marks = %v %v %v", alt.Branches[0].Terminal, alt.Branches[1].Terminal, alt.Branches[2].Terminal)
|
|
213
|
-
}
|
|
214
|
-
calls := r.consumes()
|
|
215
|
-
if len(calls) != 2 || calls[0].ID != "auth.v1/getUser" || calls[1].ID != "auth.v1/login" || calls[0].Peer != "auth.auth" {
|
|
216
|
-
t.Errorf("consumes = %+v", calls)
|
|
217
|
-
}
|
|
218
|
-
for _, diag := range b.Response().Warnings() {
|
|
219
|
-
if strings.Contains(diag.Message, "/v1/ping") {
|
|
220
|
-
continue
|
|
221
|
-
}
|
|
222
|
-
t.Errorf("unexpected diagnostic: %s", diag.Message)
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// A generated HTTP client whose api no peers line claims calls the system its
|
|
227
|
-
// vendored document is titled after: a service that vendors a document and
|
|
228
|
-
// generates a client from it is calling whatever that document describes, and
|
|
229
|
-
// nothing in this tree implements it. The manifest may still name the system
|
|
230
|
-
// itself, under externals, and then that name wins.
|
|
231
|
-
func TestAnHTTPClientNobodyClaimsCallsTheSystemItsDocumentIsTitledAfter(t *testing.T) {
|
|
232
|
-
partnerSpec := strings.Replace(vendoredSpec, "title: auth", "title: Partner Booking API", 1)
|
|
233
|
-
useCase := `package whoami
|
|
234
|
-
|
|
235
|
-
import (
|
|
236
|
-
"context"
|
|
237
|
-
|
|
238
|
-
"github.com/example/gateway/internal/infrastructure/partner/gen"
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
type UseCase struct {
|
|
242
|
-
partner gen.ClientWithResponsesInterface
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
func (uc *UseCase) Handle(ctx context.Context, in dto.Input) (dto.Output, error) {
|
|
246
|
-
_, err := uc.partner.GetUserWithResponse(ctx, in.UserID)
|
|
247
|
-
return dto.Output{}, err
|
|
248
|
-
}
|
|
249
|
-
`
|
|
250
|
-
tree := map[string]string{
|
|
251
|
-
"go.mod": "module github.com/example/gateway\n\ngo 1.27\n",
|
|
252
|
-
"internal/infrastructure/partner/gen/client.gen.go": generatedHTTPClient,
|
|
253
|
-
"internal/infrastructure/partner/gen/openapi.yaml": partnerSpec,
|
|
254
|
-
"internal/application/session/usecases/whoami/usecase.go": useCase,
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
read := func(opts flowOptions) (*catalog.Step, []catalog.RpcCall, *plugin.Builder) {
|
|
258
|
-
t.Helper()
|
|
259
|
-
root := t.TempDir()
|
|
260
|
-
writeTree(t, root, tree)
|
|
261
|
-
b := &plugin.Builder{}
|
|
262
|
-
r := newFlowReader(root, opts, b)
|
|
263
|
-
d := newDraft()
|
|
264
|
-
d.lane(r.serviceLane())
|
|
265
|
-
r.walkUseCase(d, "session/whoami", 0)
|
|
266
|
-
if len(d.steps) != 1 {
|
|
267
|
-
t.Fatalf("nodes = %q", dump(d.steps))
|
|
268
|
-
}
|
|
269
|
-
return d.steps[0].(*catalog.Step), r.consumes(), b
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
step, calls, b := read(flowOptions{context: "edge", svcID: "edge.gateway", service: "gateway"})
|
|
273
|
-
if step.Kind != catalog.StepRPC || step.To != "partner-booking" || step.Ref != "partner-booking-api.v1/getUser" || step.Status != catalog.StatusDeclared {
|
|
274
|
-
t.Errorf("rpc step = %+v", step)
|
|
275
|
-
}
|
|
276
|
-
if len(calls) != 1 || calls[0].Peer != "partner-booking" || calls[0].Status != catalog.StatusDeclared {
|
|
277
|
-
t.Errorf("consumes = %+v", calls)
|
|
278
|
-
}
|
|
279
|
-
var said bool
|
|
280
|
-
for _, warning := range b.Warnings {
|
|
281
|
-
said = said || strings.Contains(warning.Message, "read as calling partner-booking")
|
|
282
|
-
}
|
|
283
|
-
if !said {
|
|
284
|
-
t.Errorf("nothing said about the reading; warnings = %+v", b.Warnings)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
step, calls, _ = read(flowOptions{context: "edge", svcID: "edge.gateway", service: "gateway", externals: map[string]string{"partner-booking-api.v1": "partner"}})
|
|
288
|
-
if step.To != "partner" || calls[0].Peer != "partner" {
|
|
289
|
-
t.Errorf("with externals: step to %q, consumes %+v", step.To, calls)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
step, calls, _ = read(flowOptions{context: "edge", svcID: "edge.gateway", service: "gateway", peers: map[string]string{"partner-booking-api.v1": "shop.partner"}})
|
|
293
|
-
if step.To != "shop.partner" || calls[0].Peer != "shop.partner" {
|
|
294
|
-
t.Errorf("with peers: step to %q, consumes %+v", step.To, calls)
|
|
295
|
-
}
|
|
296
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import "strings"
|
|
4
|
-
|
|
5
|
-
// The id rules, in one place. Everything downstream - a table saying which
|
|
6
|
-
// aggregate it persists, a flow step naming an event, a link on a page - is a
|
|
7
|
-
// string built by one of these, so they are worth having exactly once.
|
|
8
|
-
|
|
9
|
-
func serviceID(context, service string) string { return context + "." + service }
|
|
10
|
-
|
|
11
|
-
func aggregateID(service, aggregate string) string { return service + "." + aggregate }
|
|
12
|
-
|
|
13
|
-
func blockID(aggregate, block string) string { return aggregate + "." + block }
|
|
14
|
-
|
|
15
|
-
func eventID(aggregate, name string) string { return aggregate + "." + name }
|
|
16
|
-
|
|
17
|
-
// slug turns a Go identifier into the kebab-case form ids and urls use:
|
|
18
|
-
// PriceList becomes price-list, Address becomes address, ID stays id.
|
|
19
|
-
func slug(name string) string {
|
|
20
|
-
var b strings.Builder
|
|
21
|
-
|
|
22
|
-
runes := []rune(name)
|
|
23
|
-
for i, r := range runes {
|
|
24
|
-
upper := r >= 'A' && r <= 'Z'
|
|
25
|
-
if upper && i > 0 {
|
|
26
|
-
// A dash goes in at the start of a word, which is either a lower
|
|
27
|
-
// letter followed by an upper one (orderLine) or the end of an
|
|
28
|
-
// acronym followed by a word (HTTPServer).
|
|
29
|
-
prevLower := runes[i-1] >= 'a' && runes[i-1] <= 'z'
|
|
30
|
-
nextLower := i+1 < len(runes) && runes[i+1] >= 'a' && runes[i+1] <= 'z'
|
|
31
|
-
if prevLower || nextLower {
|
|
32
|
-
b.WriteRune('-')
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if upper {
|
|
36
|
-
r = r - 'A' + 'a'
|
|
37
|
-
}
|
|
38
|
-
if r == '_' || r == '.' {
|
|
39
|
-
r = '-'
|
|
40
|
-
}
|
|
41
|
-
b.WriteRune(r)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// A separator in the source and a word boundary at the same spot would
|
|
45
|
-
// otherwise give email.Address the slug "email--address".
|
|
46
|
-
return collapseDashes(b.String())
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
func collapseDashes(s string) string {
|
|
50
|
-
for strings.Contains(s, "--") {
|
|
51
|
-
s = strings.ReplaceAll(s, "--", "-")
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return strings.Trim(s, "-")
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// camel turns a directory name into the operation id a reader expects:
|
|
58
|
-
// change_password becomes ChangePassword.
|
|
59
|
-
func camel(name string) string {
|
|
60
|
-
var b strings.Builder
|
|
61
|
-
for _, word := range strings.FieldsFunc(name, func(r rune) bool { return r == '_' || r == '-' }) {
|
|
62
|
-
runes := []rune(word)
|
|
63
|
-
if runes[0] >= 'a' && runes[0] <= 'z' {
|
|
64
|
-
runes[0] = runes[0] - 'a' + 'A'
|
|
65
|
-
}
|
|
66
|
-
b.WriteString(string(runes))
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return b.String()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// title is the human name for a package: user becomes User, price_list becomes
|
|
73
|
-
// Price List.
|
|
74
|
-
// pascal is the name a struct would have for this package: price_list holds
|
|
75
|
-
// PriceList. `title` puts the words apart for a page to read; a type name has
|
|
76
|
-
// them joined.
|
|
77
|
-
func pascal(name string) string {
|
|
78
|
-
return strings.ReplaceAll(title(name), " ", "")
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
func title(name string) string {
|
|
82
|
-
words := strings.FieldsFunc(name, func(r rune) bool { return r == '_' || r == '-' })
|
|
83
|
-
for i, word := range words {
|
|
84
|
-
runes := []rune(word)
|
|
85
|
-
if runes[0] >= 'a' && runes[0] <= 'z' {
|
|
86
|
-
runes[0] = runes[0] - 'a' + 'A'
|
|
87
|
-
}
|
|
88
|
-
words[i] = string(runes)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return strings.Join(words, " ")
|
|
92
|
-
}
|