@shortlink-org/portolan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/catalog/enum_test.go +46 -0
- package/catalog/model.go +932 -0
- package/catalog/roundtrip_test.go +185 -0
- package/catalog/via_test.go +38 -0
- package/cli/portolan.mjs +368 -0
- package/go.mod +14 -0
- package/go.sum +14 -0
- package/index.html +13 -0
- package/internal/commands/cargo.go +154 -0
- package/internal/commands/commands.go +143 -0
- package/internal/commands/commands_test.go +323 -0
- package/internal/commands/gradle.go +77 -0
- package/internal/commands/justfile.go +82 -0
- package/internal/commands/makefile.go +106 -0
- package/internal/commands/maven.go +93 -0
- package/internal/commands/packagejson.go +159 -0
- package/internal/commands/pyproject.go +169 -0
- package/internal/commands/taskfile.go +101 -0
- package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
- package/internal/commands/testdata/estate/Makefile +33 -0
- package/internal/commands/testdata/estate/Taskfile.yml +21 -0
- package/internal/commands/testdata/estate/build.gradle.kts +21 -0
- package/internal/commands/testdata/estate/justfile +20 -0
- package/internal/commands/testdata/estate/package.json +13 -0
- package/internal/commands/testdata/estate/pom.xml +29 -0
- package/internal/commands/testdata/estate/pyproject.toml +27 -0
- package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
- package/internal/commands/testdata/golden/commands.json +305 -0
- package/internal/gohttp/analyze.go +2500 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +143 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +36 -0
- package/internal/goscan/tree.go +155 -0
- package/internal/goscan/types.go +99 -0
- package/internal/wsdl/ids.go +127 -0
- package/internal/wsdl/ids_test.go +21 -0
- package/internal/wsdl/model.go +62 -0
- package/internal/wsdl/parse.go +920 -0
- package/internal/wsdl/parse_test.go +133 -0
- package/package.json +117 -0
- package/plugin/describe.go +107 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +117 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +1160 -0
- package/plugins/extract-adr/describe.go +19 -0
- package/plugins/extract-adr/describe_test.go +11 -0
- package/plugins/extract-adr/extract.go +153 -0
- package/plugins/extract-adr/extract_test.go +350 -0
- package/plugins/extract-adr/history.go +99 -0
- package/plugins/extract-adr/main.go +65 -0
- package/plugins/extract-adr/options.schema.json +26 -0
- package/plugins/extract-adr/parse.go +642 -0
- package/plugins/extract-adr/parse_test.go +405 -0
- package/plugins/extract-asyncapi/describe.go +19 -0
- package/plugins/extract-asyncapi/describe_test.go +11 -0
- package/plugins/extract-asyncapi/extract.go +315 -0
- package/plugins/extract-asyncapi/extract_test.go +197 -0
- package/plugins/extract-asyncapi/main.go +48 -0
- package/plugins/extract-asyncapi/options.schema.json +26 -0
- package/plugins/extract-asyncapi/spec.go +151 -0
- package/plugins/extract-celery/README.md +169 -0
- package/plugins/extract-celery/extract.py +346 -0
- package/plugins/extract-celery/extract_test.py +192 -0
- package/plugins/extract-celery/main.py +66 -0
- package/plugins/extract-celery/options.py +31 -0
- package/plugins/extract-celery/options.schema.json +31 -0
- package/plugins/extract-celery/producers.py +156 -0
- package/plugins/extract-celery/routes_test.py +106 -0
- package/plugins/extract-commands/describe.go +19 -0
- package/plugins/extract-commands/describe_test.go +11 -0
- package/plugins/extract-commands/extract.go +79 -0
- package/plugins/extract-commands/extract_test.go +92 -0
- package/plugins/extract-commands/main.go +55 -0
- package/plugins/extract-commands/options.schema.json +20 -0
- package/plugins/extract-csr/avro.go +258 -0
- package/plugins/extract-csr/describe.go +19 -0
- package/plugins/extract-csr/describe_test.go +11 -0
- package/plugins/extract-csr/extract.go +338 -0
- package/plugins/extract-csr/extract_test.go +374 -0
- package/plugins/extract-csr/jsonschema.go +343 -0
- package/plugins/extract-csr/lock.go +25 -0
- package/plugins/extract-csr/main.go +84 -0
- package/plugins/extract-csr/options.schema.json +43 -0
- package/plugins/extract-csr/subject.go +102 -0
- package/plugins/extract-django/README.md +386 -0
- package/plugins/extract-django/apps.py +100 -0
- package/plugins/extract-django/auth.py +302 -0
- package/plugins/extract-django/auth_test.py +188 -0
- package/plugins/extract-django/clients.py +121 -0
- package/plugins/extract-django/contracts.py +458 -0
- package/plugins/extract-django/database.py +119 -0
- package/plugins/extract-django/domain.py +318 -0
- package/plugins/extract-django/events.py +86 -0
- package/plugins/extract-django/extract.py +601 -0
- package/plugins/extract-django/extract_test.py +397 -0
- package/plugins/extract-django/flows.py +1278 -0
- package/plugins/extract-django/flows_test.py +338 -0
- package/plugins/extract-django/ids.py +42 -0
- package/plugins/extract-django/ids_test.py +30 -0
- package/plugins/extract-django/lifecycle.py +222 -0
- package/plugins/extract-django/lifecycle_test.py +76 -0
- package/plugins/extract-django/main.py +66 -0
- package/plugins/extract-django/openapi.py +229 -0
- package/plugins/extract-django/openapi_test.py +60 -0
- package/plugins/extract-django/operations.py +96 -0
- package/plugins/extract-django/options.py +61 -0
- package/plugins/extract-django/options.schema.json +126 -0
- package/plugins/extract-django/routing.py +214 -0
- package/plugins/extract-django/routing_test.py +198 -0
- package/plugins/extract-django/serializers.py +558 -0
- package/plugins/extract-django/store.py +434 -0
- package/plugins/extract-django/transport.py +242 -0
- package/plugins/extract-flows/describe.go +19 -0
- package/plugins/extract-flows/describe_test.go +11 -0
- package/plugins/extract-flows/extract.go +91 -0
- package/plugins/extract-flows/main.go +45 -0
- package/plugins/extract-flows/options.schema.json +16 -0
- package/plugins/extract-flows/parse.go +593 -0
- package/plugins/extract-flows/parse_test.go +204 -0
- package/plugins/extract-glossary/describe.go +19 -0
- package/plugins/extract-glossary/describe_test.go +11 -0
- package/plugins/extract-glossary/extract.go +115 -0
- package/plugins/extract-glossary/extract_test.go +220 -0
- package/plugins/extract-glossary/main.go +59 -0
- package/plugins/extract-glossary/options.schema.json +21 -0
- package/plugins/extract-glossary/parse.go +214 -0
- package/plugins/extract-glossary/parse_test.go +203 -0
- package/plugins/extract-go/aggregate.go +214 -0
- package/plugins/extract-go/client.go +409 -0
- package/plugins/extract-go/client_test.go +266 -0
- package/plugins/extract-go/describe.go +19 -0
- package/plugins/extract-go/describe_test.go +11 -0
- package/plugins/extract-go/enum.go +195 -0
- package/plugins/extract-go/enum_test.go +82 -0
- package/plugins/extract-go/event.go +99 -0
- package/plugins/extract-go/extract.go +191 -0
- package/plugins/extract-go/extract_test.go +261 -0
- package/plugins/extract-go/flow.go +1441 -0
- package/plugins/extract-go/flow_test.go +609 -0
- package/plugins/extract-go/httpclient.go +174 -0
- package/plugins/extract-go/httpclient_test.go +296 -0
- package/plugins/extract-go/ids.go +92 -0
- package/plugins/extract-go/layout.go +225 -0
- package/plugins/extract-go/layout_test.go +125 -0
- package/plugins/extract-go/lifecycle.go +324 -0
- package/plugins/extract-go/lifecycle_test.go +108 -0
- package/plugins/extract-go/main.go +79 -0
- package/plugins/extract-go/operation.go +157 -0
- package/plugins/extract-go/options.schema.json +66 -0
- package/plugins/extract-go/source.go +316 -0
- package/plugins/extract-go/transport.go +321 -0
- package/plugins/extract-go/transport_test.go +145 -0
- package/plugins/extract-go/wiring.go +434 -0
- package/plugins/extract-go-nats/describe.go +19 -0
- package/plugins/extract-go-nats/describe_test.go +11 -0
- package/plugins/extract-go-nats/extract.go +177 -0
- package/plugins/extract-go-nats/extract_test.go +389 -0
- package/plugins/extract-go-nats/index.go +379 -0
- package/plugins/extract-go-nats/main.go +42 -0
- package/plugins/extract-go-nats/options.schema.json +21 -0
- package/plugins/extract-go-nats/resolve.go +161 -0
- package/plugins/extract-go-nats/sites.go +224 -0
- package/plugins/extract-graphql/describe.go +19 -0
- package/plugins/extract-graphql/describe_test.go +11 -0
- package/plugins/extract-graphql/extract.go +433 -0
- package/plugins/extract-graphql/extract_test.go +256 -0
- package/plugins/extract-graphql/ids.go +49 -0
- package/plugins/extract-graphql/lex.go +237 -0
- package/plugins/extract-graphql/main.go +51 -0
- package/plugins/extract-graphql/options.schema.json +31 -0
- package/plugins/extract-graphql/parse.go +621 -0
- package/plugins/extract-graphql/parse_test.go +122 -0
- package/plugins/extract-http-clients/describe.go +19 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +705 -0
- package/plugins/extract-http-clients/extract_test.go +1263 -0
- package/plugins/extract-http-clients/main.go +42 -0
- package/plugins/extract-http-clients/options.schema.json +33 -0
- package/plugins/extract-java/README.md +190 -0
- package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
- package/plugins/extract-java/options.schema.json +70 -0
- package/plugins/extract-openapi/describe.go +19 -0
- package/plugins/extract-openapi/describe_test.go +11 -0
- package/plugins/extract-openapi/discover.go +243 -0
- package/plugins/extract-openapi/extract.go +526 -0
- package/plugins/extract-openapi/extract_test.go +545 -0
- package/plugins/extract-openapi/main.go +75 -0
- package/plugins/extract-openapi/options.schema.json +62 -0
- package/plugins/extract-openapi/spec.go +350 -0
- package/plugins/extract-project/README.md +22 -0
- package/plugins/extract-project/describe.go +19 -0
- package/plugins/extract-project/describe_test.go +11 -0
- package/plugins/extract-project/extract.go +221 -0
- package/plugins/extract-project/extract_test.go +109 -0
- package/plugins/extract-project/main.go +41 -0
- package/plugins/extract-project/options.schema.json +48 -0
- package/plugins/extract-proto/ast.go +125 -0
- package/plugins/extract-proto/consumes.go +77 -0
- package/plugins/extract-proto/describe.go +19 -0
- package/plugins/extract-proto/extract.go +293 -0
- package/plugins/extract-proto/extract_test.go +459 -0
- package/plugins/extract-proto/ids.go +89 -0
- package/plugins/extract-proto/ids_test.go +57 -0
- package/plugins/extract-proto/lex.go +285 -0
- package/plugins/extract-proto/main.go +100 -0
- package/plugins/extract-proto/module.go +120 -0
- package/plugins/extract-proto/options.schema.json +51 -0
- package/plugins/extract-proto/parse.go +720 -0
- package/plugins/extract-proto/parse_test.go +307 -0
- package/plugins/extract-proto/provides.go +236 -0
- package/plugins/extract-proto/resolve.go +222 -0
- package/plugins/extract-proto/resolve_test.go +119 -0
- package/plugins/extract-python-kafka/README.md +36 -0
- package/plugins/extract-python-kafka/extract.py +211 -0
- package/plugins/extract-python-kafka/extract_test.py +68 -0
- package/plugins/extract-python-kafka/main.py +59 -0
- package/plugins/extract-python-kafka/options.py +29 -0
- package/plugins/extract-python-kafka/options.schema.json +31 -0
- package/plugins/extract-redis/README.md +11 -0
- package/plugins/extract-redis/describe.go +19 -0
- package/plugins/extract-redis/describe_test.go +11 -0
- package/plugins/extract-redis/extract.go +183 -0
- package/plugins/extract-redis/extract_test.go +168 -0
- package/plugins/extract-redis/keyspaces.go +469 -0
- package/plugins/extract-redis/main.go +44 -0
- package/plugins/extract-redis/options.schema.json +30 -0
- package/plugins/extract-river/describe.go +19 -0
- package/plugins/extract-river/describe_test.go +11 -0
- package/plugins/extract-river/extract.go +507 -0
- package/plugins/extract-river/extract_test.go +132 -0
- package/plugins/extract-river/main.go +42 -0
- package/plugins/extract-river/options.schema.json +21 -0
- package/plugins/extract-rust/Cargo.lock +133 -0
- package/plugins/extract-rust/Cargo.toml +23 -0
- package/plugins/extract-rust/README.md +204 -0
- package/plugins/extract-rust/options.schema.json +65 -0
- package/plugins/extract-rust/rustfmt.toml +1 -0
- package/plugins/extract-sql/ddl.go +893 -0
- package/plugins/extract-sql/ddl_test.go +401 -0
- package/plugins/extract-sql/describe.go +19 -0
- package/plugins/extract-sql/describe_test.go +11 -0
- package/plugins/extract-sql/layout.go +221 -0
- package/plugins/extract-sql/layout_test.go +55 -0
- package/plugins/extract-sql/lineage.go +107 -0
- package/plugins/extract-sql/main.go +141 -0
- package/plugins/extract-sql/maps.go +564 -0
- package/plugins/extract-sql/maps_java.go +117 -0
- package/plugins/extract-sql/maps_rust.go +333 -0
- package/plugins/extract-sql/maps_rust_test.go +70 -0
- package/plugins/extract-sql/maps_test.go +204 -0
- package/plugins/extract-sql/maps_ts.go +398 -0
- package/plugins/extract-sql/maps_ts_test.go +136 -0
- package/plugins/extract-sql/options.schema.json +53 -0
- package/plugins/extract-sql/projection.go +70 -0
- package/plugins/extract-sql/projection_test.go +50 -0
- package/plugins/extract-sql/store.go +420 -0
- package/plugins/extract-sql/store_test.go +233 -0
- package/plugins/extract-sql/view.go +295 -0
- package/plugins/extract-ts/README.md +306 -0
- package/plugins/extract-ts/ast.test.ts +178 -0
- package/plugins/extract-ts/ast.ts +648 -0
- package/plugins/extract-ts/clients.ts +185 -0
- package/plugins/extract-ts/domain.ts +164 -0
- package/plugins/extract-ts/extract.test.ts +208 -0
- package/plugins/extract-ts/extract.ts +200 -0
- package/plugins/extract-ts/flows.ts +783 -0
- package/plugins/extract-ts/graphql.test.ts +73 -0
- package/plugins/extract-ts/graphql.ts +181 -0
- package/plugins/extract-ts/ids.test.ts +21 -0
- package/plugins/extract-ts/ids.ts +52 -0
- package/plugins/extract-ts/lifecycle.ts +138 -0
- package/plugins/extract-ts/main.ts +48 -0
- package/plugins/extract-ts/openapi.test.ts +64 -0
- package/plugins/extract-ts/openapi.ts +99 -0
- package/plugins/extract-ts/operations.ts +93 -0
- package/plugins/extract-ts/options.schema.json +96 -0
- package/plugins/extract-ts/source.test.ts +174 -0
- package/plugins/extract-ts/source.ts +465 -0
- package/plugins/extract-ts/transport.ts +147 -0
- package/plugins/extract-ts/tsconfig.json +13 -0
- package/plugins/extract-ts/wiring.ts +190 -0
- package/plugins/extract-watermill/describe.go +19 -0
- package/plugins/extract-watermill/describe_test.go +11 -0
- package/plugins/extract-watermill/extract.go +1228 -0
- package/plugins/extract-watermill/extract_test.go +234 -0
- package/plugins/extract-watermill/main.go +41 -0
- package/plugins/extract-watermill/options.schema.json +21 -0
- package/plugins/extract-wsdl/describe.go +19 -0
- package/plugins/extract-wsdl/describe_test.go +11 -0
- package/plugins/extract-wsdl/extract.go +148 -0
- package/plugins/extract-wsdl/extract_test.go +45 -0
- package/plugins/extract-wsdl/main.go +53 -0
- package/plugins/extract-wsdl/options.schema.json +59 -0
- package/plugins/fetch-bsr/auth.go +114 -0
- package/plugins/fetch-bsr/bsr.go +240 -0
- package/plugins/fetch-bsr/cache.go +66 -0
- package/plugins/fetch-bsr/describe.go +19 -0
- package/plugins/fetch-bsr/fetch.go +192 -0
- package/plugins/fetch-bsr/fetch_test.go +465 -0
- package/plugins/fetch-bsr/lock.go +71 -0
- package/plugins/fetch-bsr/main.go +117 -0
- package/plugins/fetch-bsr/options.schema.json +42 -0
- package/plugins/fetch-csr/auth.go +95 -0
- package/plugins/fetch-csr/cache.go +69 -0
- package/plugins/fetch-csr/describe.go +19 -0
- package/plugins/fetch-csr/describe_test.go +11 -0
- package/plugins/fetch-csr/fetch.go +254 -0
- package/plugins/fetch-csr/fetch_test.go +494 -0
- package/plugins/fetch-csr/lock.go +95 -0
- package/plugins/fetch-csr/main.go +122 -0
- package/plugins/fetch-csr/options.schema.json +42 -0
- package/plugins/fetch-csr/registry.go +209 -0
- package/plugins/fetch-git/cache.go +65 -0
- package/plugins/fetch-git/describe.go +19 -0
- package/plugins/fetch-git/describe_test.go +11 -0
- package/plugins/fetch-git/fetch.go +181 -0
- package/plugins/fetch-git/fetch_test.go +332 -0
- package/plugins/fetch-git/git.go +169 -0
- package/plugins/fetch-git/lock.go +72 -0
- package/plugins/fetch-git/main.go +127 -0
- package/plugins/fetch-git/offline.go +39 -0
- package/plugins/fetch-git/options.schema.json +42 -0
- package/plugins/fetch-git/pin.go +90 -0
- package/plugins/fetch-git/pin_test.go +74 -0
- package/plugins/gen-backstage/describe.go +17 -0
- package/plugins/gen-backstage/main.go +22 -0
- package/plugins/gen-backstage/options.schema.json +30 -0
- package/plugins/gen-backstage/plugin.go +473 -0
- package/plugins/gen-backstage/plugin_test.go +145 -0
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +162 -0
- package/plugins/gen-markdown/aggregate.go +362 -0
- package/plugins/gen-markdown/canonical.go +93 -0
- package/plugins/gen-markdown/context.go +90 -0
- package/plugins/gen-markdown/coverage_test.go +89 -0
- package/plugins/gen-markdown/describe.go +19 -0
- package/plugins/gen-markdown/describe_test.go +11 -0
- package/plugins/gen-markdown/external.go +71 -0
- package/plugins/gen-markdown/flow.go +277 -0
- package/plugins/gen-markdown/glossary.go +76 -0
- package/plugins/gen-markdown/glossary_test.go +148 -0
- package/plugins/gen-markdown/llms.go +302 -0
- package/plugins/gen-markdown/main.go +22 -0
- package/plugins/gen-markdown/markdown.go +254 -0
- package/plugins/gen-markdown/markdown_test.go +100 -0
- package/plugins/gen-markdown/module.go +107 -0
- package/plugins/gen-markdown/options.schema.json +16 -0
- package/plugins/gen-markdown/plugin.go +39 -0
- package/plugins/gen-markdown/quality_test.go +176 -0
- package/plugins/gen-markdown/redis_test.go +38 -0
- package/plugins/gen-markdown/render.go +344 -0
- package/plugins/gen-markdown/render_test.go +186 -0
- package/plugins/gen-markdown/service.go +456 -0
- package/plugins/gen-markdown/source.go +177 -0
- package/plugins/gen-markdown/store.go +201 -0
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +17 -0
- package/plugins/gen-mermaid/main.go +22 -0
- package/plugins/gen-mermaid/options.schema.json +10 -0
- package/plugins/gen-mermaid/plugin.go +104 -0
- package/plugins/gen-mermaid/plugin_test.go +33 -0
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/pyplugin/catalog.py +209 -0
- package/plugins/pyplugin/celery_conf.py +436 -0
- package/plugins/pyplugin/celery_tasks.py +76 -0
- package/plugins/pyplugin/kafka.py +570 -0
- package/plugins/pyplugin/names.py +38 -0
- package/plugins/pyplugin/protocol.py +79 -0
- package/plugins/pyplugin/source.py +285 -0
- package/plugins/verify-codeowners/describe.go +19 -0
- package/plugins/verify-codeowners/describe_test.go +11 -0
- package/plugins/verify-codeowners/main.go +75 -0
- package/plugins/verify-codeowners/match.go +85 -0
- package/plugins/verify-codeowners/match_test.go +47 -0
- package/plugins/verify-codeowners/options.schema.json +15 -0
- package/plugins/verify-codeowners/owners.go +164 -0
- package/plugins/verify-codeowners/owners_test.go +225 -0
- package/plugins/verify-codeowners/parse.go +90 -0
- package/plugins/verify-codeowners/parse_test.go +62 -0
- package/plugins/verify-otel/describe.go +19 -0
- package/plugins/verify-otel/describe_test.go +11 -0
- package/plugins/verify-otel/main.go +53 -0
- package/plugins/verify-otel/match.go +336 -0
- package/plugins/verify-otel/options.schema.json +30 -0
- package/plugins/verify-otel/otlp.go +200 -0
- package/plugins/verify-otel/verify.go +734 -0
- package/plugins/verify-otel/verify_test.go +460 -0
- package/portolan.json +1073 -0
- package/public/favicon.svg +12 -0
- package/schema/portolan.schema.json +2558 -0
- package/scripts/README.md +53 -0
- package/scripts/build-report.mjs +60 -0
- package/scripts/build-report.test.mjs +44 -0
- package/scripts/builtin-plugins.mjs +49 -0
- package/scripts/catalog-sources.mjs +67 -0
- package/scripts/diff.mjs +315 -0
- package/scripts/diff.test.mjs +151 -0
- package/scripts/forge-comment.mjs +144 -0
- package/scripts/forge-comment.test.mjs +151 -0
- package/scripts/forge-release.mjs +138 -0
- package/scripts/forge-release.test.mjs +106 -0
- package/scripts/forge.mjs +61 -0
- package/scripts/gen-likec4.mjs +854 -0
- package/scripts/gen-likec4.test.mjs +258 -0
- package/scripts/gen.mjs +603 -0
- package/scripts/generated-docs.test.mjs +66 -0
- package/scripts/local-api.mjs +1192 -0
- package/scripts/local-api.test.mjs +387 -0
- package/scripts/manifest.mjs +214 -0
- package/scripts/manifest.test.mjs +148 -0
- package/scripts/output-path.mjs +79 -0
- package/scripts/output-path.test.mjs +45 -0
- package/scripts/package-smoke.mjs +37 -0
- package/scripts/plugin-host.mjs +419 -0
- package/scripts/plugin-host.test.mjs +109 -0
- package/scripts/plugin-wasm-worker.mjs +34 -0
- package/scripts/run-builtin.mjs +81 -0
- package/scripts/schema.mjs +357 -0
- package/scripts/site-docs.mjs +295 -0
- package/scripts/site-docs.test.mjs +181 -0
- package/scripts/vendor-lock.mjs +58 -0
- package/scripts/vendor-lock.test.mjs +69 -0
- package/src/README.md +38 -0
- package/src/app/App.tsx +448 -0
- package/src/app/BranchPicker.test.ts +46 -0
- package/src/app/BranchPicker.tsx +153 -0
- package/src/app/Breadcrumbs.test.ts +38 -0
- package/src/app/Breadcrumbs.tsx +223 -0
- package/src/app/BuildStamp.tsx +146 -0
- package/src/app/CommandPalette.tsx +292 -0
- package/src/app/HashScroll.tsx +45 -0
- package/src/app/Sidebar.tsx +1742 -0
- package/src/app/TopBar.tsx +393 -0
- package/src/app/density.tsx +64 -0
- package/src/app/forge-access.test.ts +27 -0
- package/src/app/forge-access.tsx +80 -0
- package/src/app/panels.tsx +141 -0
- package/src/app/pins.tsx +242 -0
- package/src/app/query-client.ts +24 -0
- package/src/app/responsive.ts +55 -0
- package/src/app/shortcuts.test.ts +39 -0
- package/src/app/shortcuts.tsx +282 -0
- package/src/app/theme.tsx +51 -0
- package/src/app/title.test.ts +14 -0
- package/src/app/title.ts +30 -0
- package/src/app/toast.tsx +59 -0
- package/src/app/tree-keys.test.ts +80 -0
- package/src/app/tree-keys.ts +87 -0
- package/src/app/ui-store.ts +52 -0
- package/src/catalog-error.test.ts +105 -0
- package/src/catalog-profile.test.ts +59 -0
- package/src/catalog-profile.ts +101 -0
- package/src/catalog-stores.test.ts +431 -0
- package/src/catalog.test.ts +1215 -0
- package/src/catalog.ts +3125 -0
- package/src/chat/Cards.tsx +408 -0
- package/src/chat/ChatPanel.tsx +79 -0
- package/src/chat/ChatSettings.tsx +219 -0
- package/src/chat/Composer.tsx +99 -0
- package/src/chat/Conversation.tsx +281 -0
- package/src/chat/Header.tsx +46 -0
- package/src/chat/Message.tsx +49 -0
- package/src/chat/Notice.tsx +195 -0
- package/src/chat/Starter.tsx +67 -0
- package/src/chat/Steps.tsx +45 -0
- package/src/chat/Waiting.tsx +87 -0
- package/src/chat/flags.test.ts +59 -0
- package/src/chat/flags.ts +155 -0
- package/src/chat/linkify.test.ts +30 -0
- package/src/chat/linkify.ts +45 -0
- package/src/chat/prefs.ts +56 -0
- package/src/chat/prompt.test.ts +43 -0
- package/src/chat/prompt.ts +157 -0
- package/src/chat/store.ts +35 -0
- package/src/chat/tools.ts +58 -0
- package/src/chat/transport.ts +70 -0
- package/src/components/AdrRow.tsx +29 -0
- package/src/components/AnchorLink.tsx +67 -0
- package/src/components/ApiReference.tsx +134 -0
- package/src/components/AsyncApiReference.tsx +120 -0
- package/src/components/CatalogStamp.tsx +155 -0
- package/src/components/ChannelRows.tsx +132 -0
- package/src/components/CommandRows.tsx +139 -0
- package/src/components/DiagramSkeleton.tsx +71 -0
- package/src/components/EditorLink.tsx +85 -0
- package/src/components/FieldTree.tsx +305 -0
- package/src/components/FlowTrigger.tsx +34 -0
- package/src/components/Ident.tsx +89 -0
- package/src/components/Integrations.tsx +243 -0
- package/src/components/LifecycleDiagram.tsx +110 -0
- package/src/components/MachineDocs.tsx +42 -0
- package/src/components/Markdown.tsx +173 -0
- package/src/components/MarkdownTable.tsx +206 -0
- package/src/components/Mermaid.tsx +211 -0
- package/src/components/MethodRows.tsx +309 -0
- package/src/components/Overlay.tsx +125 -0
- package/src/components/PageHeader.tsx +189 -0
- package/src/components/RowActions.tsx +97 -0
- package/src/components/SchemaDocument.tsx +89 -0
- package/src/components/Select.tsx +126 -0
- package/src/components/ShapeRows.tsx +118 -0
- package/src/components/SourceDoc.tsx +82 -0
- package/src/components/SourcePreview.tsx +441 -0
- package/src/components/TabRow.tsx +182 -0
- package/src/components/TechIcon.tsx +34 -0
- package/src/components/Toc.tsx +132 -0
- package/src/components/WhatLinksHere.tsx +273 -0
- package/src/components/WsdlReference.tsx +84 -0
- package/src/components/ddd-icons.tsx +107 -0
- package/src/components/kind.tsx +157 -0
- package/src/components/logo.tsx +61 -0
- package/src/components/primitives.tsx +264 -0
- package/src/data.test.ts +18 -0
- package/src/data.ts +202 -0
- package/src/enrich.test.ts +1150 -0
- package/src/enrich.ts +672 -0
- package/src/er/ErCanvas.tsx +533 -0
- package/src/er/RedisSchema.tsx +74 -0
- package/src/er/RelationCard.tsx +267 -0
- package/src/er/StoreHeader.tsx +155 -0
- package/src/er/TableNode.tsx +84 -0
- package/src/er/ViewNode.tsx +83 -0
- package/src/er/layout.test.ts +115 -0
- package/src/er/layout.ts +77 -0
- package/src/er/lineage.test.ts +140 -0
- package/src/er/lineage.ts +107 -0
- package/src/er/markers.tsx +79 -0
- package/src/er/spec.test.ts +344 -0
- package/src/er/spec.ts +426 -0
- package/src/flow/ChainList.tsx +162 -0
- package/src/flow/FlowTable.tsx +123 -0
- package/src/flow/FlowToolbar.tsx +372 -0
- package/src/flow/StepDetail.tsx +380 -0
- package/src/flow/StepRail.tsx +428 -0
- package/src/flow/answers.test.ts +50 -0
- package/src/flow/answers.ts +58 -0
- package/src/flow/chain.test.ts +383 -0
- package/src/flow/chain.ts +353 -0
- package/src/flow/chapters.test.ts +185 -0
- package/src/flow/chapters.ts +225 -0
- package/src/flow/continues.test.ts +102 -0
- package/src/flow/continues.ts +65 -0
- package/src/flow/cross-context.test.ts +97 -0
- package/src/flow/cross-context.ts +35 -0
- package/src/flow/mermaid.test.ts +22 -0
- package/src/flow/mermaid.ts +141 -0
- package/src/flow/outline.test.ts +212 -0
- package/src/flow/outline.ts +170 -0
- package/src/flow/paths.test.ts +129 -0
- package/src/flow/paths.ts +147 -0
- package/src/flow/prefs.test.ts +80 -0
- package/src/flow/prefs.ts +104 -0
- package/src/graph/DependencyGraph.tsx +283 -0
- package/src/graph/DependencyNodes.tsx +162 -0
- package/src/graph/EdgeLabel.tsx +61 -0
- package/src/graph/FocusedEventGraph.tsx +211 -0
- package/src/graph/GraphToolbar.tsx +187 -0
- package/src/graph/RoutedEdge.tsx +80 -0
- package/src/graph/dependency-layout.test.ts +360 -0
- package/src/graph/dependency-layout.ts +444 -0
- package/src/graph/elk.test.ts +70 -0
- package/src/graph/elk.ts +287 -0
- package/src/graph/nodes.tsx +97 -0
- package/src/graph/theme.ts +81 -0
- package/src/graph/useElkFlow.ts +125 -0
- package/src/index.css +1547 -0
- package/src/language/InLanguage.tsx +40 -0
- package/src/language/TermCard.tsx +132 -0
- package/src/language/cards.test.ts +114 -0
- package/src/language/cards.ts +111 -0
- package/src/lib/admonition.test.ts +49 -0
- package/src/lib/admonition.ts +48 -0
- package/src/lib/adr.test.ts +131 -0
- package/src/lib/adr.ts +68 -0
- package/src/lib/api.test.ts +122 -0
- package/src/lib/api.ts +135 -0
- package/src/lib/backlinks.test.ts +330 -0
- package/src/lib/backlinks.ts +567 -0
- package/src/lib/branch-compare.ts +16 -0
- package/src/lib/build-info.test.ts +76 -0
- package/src/lib/build-info.ts +80 -0
- package/src/lib/catalog-diff.test.ts +275 -0
- package/src/lib/catalog-diff.ts +655 -0
- package/src/lib/clipboard.ts +43 -0
- package/src/lib/comparison-memory.test.ts +40 -0
- package/src/lib/comparison-memory.ts +39 -0
- package/src/lib/context-color.ts +27 -0
- package/src/lib/context-map.test.ts +148 -0
- package/src/lib/context-map.ts +399 -0
- package/src/lib/data-model.test.ts +245 -0
- package/src/lib/data-model.ts +399 -0
- package/src/lib/data-problems.test.ts +178 -0
- package/src/lib/data-problems.ts +314 -0
- package/src/lib/derive.test.ts +251 -0
- package/src/lib/derive.ts +515 -0
- package/src/lib/editor-link.test.ts +125 -0
- package/src/lib/editor-link.ts +96 -0
- package/src/lib/editor-prefs.ts +27 -0
- package/src/lib/event-graph.test.ts +237 -0
- package/src/lib/event-graph.ts +327 -0
- package/src/lib/export-canvas.ts +37 -0
- package/src/lib/export-file.test.ts +30 -0
- package/src/lib/export-file.ts +20 -0
- package/src/lib/flow-tree.test.ts +177 -0
- package/src/lib/flow-tree.ts +160 -0
- package/src/lib/forge-refs.test.ts +50 -0
- package/src/lib/forge-refs.ts +60 -0
- package/src/lib/format.test.ts +35 -0
- package/src/lib/format.ts +64 -0
- package/src/lib/github-catalog.test.ts +301 -0
- package/src/lib/github-catalog.ts +610 -0
- package/src/lib/integrations.test.ts +119 -0
- package/src/lib/integrations.ts +133 -0
- package/src/lib/kinds.test.ts +91 -0
- package/src/lib/kinds.ts +204 -0
- package/src/lib/lifecycle.test.ts +93 -0
- package/src/lib/lifecycle.ts +165 -0
- package/src/lib/local-api.ts +194 -0
- package/src/lib/motion-features.ts +11 -0
- package/src/lib/motion.test.ts +120 -0
- package/src/lib/motion.tsx +269 -0
- package/src/lib/palette-recent.test.ts +80 -0
- package/src/lib/palette-recent.ts +90 -0
- package/src/lib/palette.test.ts +365 -0
- package/src/lib/palette.ts +593 -0
- package/src/lib/pins.test.ts +139 -0
- package/src/lib/pins.ts +130 -0
- package/src/lib/problems.test.ts +112 -0
- package/src/lib/project-preview.ts +5 -0
- package/src/lib/proto-problems.test.ts +121 -0
- package/src/lib/proto-problems.ts +65 -0
- package/src/lib/queries.test.ts +113 -0
- package/src/lib/queries.ts +131 -0
- package/src/lib/registry.test.ts +219 -0
- package/src/lib/registry.ts +208 -0
- package/src/lib/scenarios.ts +547 -0
- package/src/lib/setup-info.test.ts +199 -0
- package/src/lib/setup-info.ts +415 -0
- package/src/lib/shape.test.ts +289 -0
- package/src/lib/shape.ts +454 -0
- package/src/lib/sidebar-prefs.test.ts +32 -0
- package/src/lib/sidebar-prefs.ts +75 -0
- package/src/lib/source-code.test.ts +159 -0
- package/src/lib/source-code.ts +286 -0
- package/src/lib/source-doc.test.ts +131 -0
- package/src/lib/source-doc.ts +103 -0
- package/src/lib/source-highlight.test.ts +29 -0
- package/src/lib/source-highlight.ts +24 -0
- package/src/lib/source-link.test.ts +153 -0
- package/src/lib/source-link.ts +201 -0
- package/src/lib/spec-files.test.ts +24 -0
- package/src/lib/spec-files.ts +23 -0
- package/src/lib/table-directive.test.ts +139 -0
- package/src/lib/table-directive.ts +106 -0
- package/src/lib/tech.test.ts +120 -0
- package/src/lib/tech.ts +421 -0
- package/src/lib/terms.test.ts +67 -0
- package/src/lib/terms.ts +179 -0
- package/src/lib/wire-problems.test.ts +263 -0
- package/src/lib/wire-problems.ts +281 -0
- package/src/likec4/C4View.tsx +55 -0
- package/src/likec4/CanvasBridge.tsx +187 -0
- package/src/likec4/FlowView.tsx +235 -0
- package/src/likec4/InteractiveView.tsx +144 -0
- package/src/likec4/canvas-viewport.test.ts +149 -0
- package/src/likec4/canvas-viewport.ts +135 -0
- package/src/likec4/flow-edges.test.ts +51 -0
- package/src/likec4/flow-edges.ts +89 -0
- package/src/likec4/frame-theme.test.ts +76 -0
- package/src/likec4/frame-theme.ts +101 -0
- package/src/likec4/highlight-css.test.ts +49 -0
- package/src/likec4/highlight-css.ts +52 -0
- package/src/likec4/ids.test.ts +151 -0
- package/src/likec4/ids.ts +115 -0
- package/src/likec4/levels.tsx +70 -0
- package/src/likec4/mapping.test.ts +66 -0
- package/src/likec4/mapping.ts +54 -0
- package/src/likec4/reserved.json +141 -0
- package/src/likec4/view-index.ts +78 -0
- package/src/main.tsx +13 -0
- package/src/map/ContextMapGraph.tsx +274 -0
- package/src/map/ContextNode.tsx +70 -0
- package/src/map/FloatingEdge.tsx +86 -0
- package/src/merge.test.ts +1038 -0
- package/src/merge.ts +1004 -0
- package/src/pages/AdrDetail.tsx +242 -0
- package/src/pages/AdrIndex.tsx +114 -0
- package/src/pages/AggregatePage.tsx +727 -0
- package/src/pages/BlockPage.tsx +478 -0
- package/src/pages/CatalogFailure.tsx +111 -0
- package/src/pages/Changes.tsx +313 -0
- package/src/pages/ContextMap.tsx +534 -0
- package/src/pages/ContextPage.tsx +346 -0
- package/src/pages/EnumPage.tsx +282 -0
- package/src/pages/EventPage.tsx +686 -0
- package/src/pages/ExternalPage.tsx +204 -0
- package/src/pages/FlowDetail.tsx +663 -0
- package/src/pages/FlowIndex.tsx +303 -0
- package/src/pages/GraphPage.tsx +217 -0
- package/src/pages/Language.tsx +257 -0
- package/src/pages/ModulePage.tsx +366 -0
- package/src/pages/NotFound.tsx +54 -0
- package/src/pages/Overview.tsx +344 -0
- package/src/pages/Problems.tsx +360 -0
- package/src/pages/RegistryIndex.tsx +118 -0
- package/src/pages/ServicePage.tsx +746 -0
- package/src/pages/Settings.tsx +975 -0
- package/src/pages/StorePage.tsx +114 -0
- package/src/peek/PeekCard.test.ts +35 -0
- package/src/peek/PeekCard.tsx +118 -0
- package/src/peek/PeekLayer.tsx +147 -0
- package/src/peek/model.test.ts +156 -0
- package/src/peek/model.ts +253 -0
- package/src/routes.test.ts +222 -0
- package/src/routes.ts +449 -0
- package/src/selection/DetailPanel.tsx +1407 -0
- package/src/selection/hash.test.ts +73 -0
- package/src/selection/hash.ts +58 -0
- package/src/selection/model.test.ts +199 -0
- package/src/selection/model.ts +473 -0
- package/src/selection/pages.test.ts +115 -0
- package/src/selection/pages.ts +268 -0
- package/src/selection/store.test.ts +62 -0
- package/src/selection/store.ts +46 -0
- package/src/selection/sync.tsx +99 -0
- package/src/table/ColumnsMenu.tsx +72 -0
- package/src/table/DataTable.tsx +780 -0
- package/src/table/Facets.tsx +54 -0
- package/src/table/GroupMenu.tsx +69 -0
- package/src/table/Toolbar.tsx +190 -0
- package/src/table/cells.tsx +117 -0
- package/src/table/compare.test.ts +117 -0
- package/src/table/compare.ts +196 -0
- package/src/table/export.test.ts +88 -0
- package/src/table/export.ts +66 -0
- package/src/table/facet-groups.test.ts +74 -0
- package/src/table/facet-groups.ts +56 -0
- package/src/table/grouping.test.ts +84 -0
- package/src/table/grouping.ts +82 -0
- package/src/table/infer.test.ts +57 -0
- package/src/table/infer.ts +50 -0
- package/src/table/persist.test.ts +76 -0
- package/src/table/persist.ts +112 -0
- package/src/table/sort-url.test.ts +75 -0
- package/src/table/sort-url.ts +52 -0
- package/src/table/types.ts +80 -0
- package/src/table/useTableState.ts +220 -0
- package/src/test-catalog.ts +7 -0
- package/src/testing/estate/data__catalog.json +2847 -0
- package/src/testing/estate/data__flows.json +1724 -0
- package/src/testing/estate/examples__auth__portolan__api.json +212 -0
- package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
- package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
- package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
- package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
- package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
- package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
- package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
- package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
- package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
- package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
- package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
- package/src/testing/estate.ts +49 -0
- package/src/trail/Trail.tsx +81 -0
- package/src/trail/model.test.ts +157 -0
- package/src/trail/model.ts +189 -0
- package/src/trail/record.tsx +32 -0
- package/src/trail/store.test.ts +66 -0
- package/src/trail/store.ts +102 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +171 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
const sample = `# Order accepted
|
|
11
|
+
owner: shop
|
|
12
|
+
source: services/oms/test/integration/order_accepted_test.go
|
|
13
|
+
|
|
14
|
+
The narrow slice one integration test pins end to end: an order commits
|
|
15
|
+
with its outbox row.
|
|
16
|
+
|
|
17
|
+
Every hop here is asserted.
|
|
18
|
+
|
|
19
|
+
## Participants
|
|
20
|
+
- oms-db: store in shop "oms-db (postgres)"
|
|
21
|
+
- psp-gateway: external "psp-gateway (external)"
|
|
22
|
+
- customer: actor
|
|
23
|
+
|
|
24
|
+
## Steps
|
|
25
|
+
// the request
|
|
26
|
+
customer -> shop.oms: rpc PlaceOrder [verified] @internal/oms/http/orders.go:40
|
|
27
|
+
shop.oms -> oms-db: insertOrderAndOutboxRow [verified] @internal/oms/adapter/postgres/order_repo.go:141 #a1
|
|
28
|
+
> The order row and the OrderPlaced outbox row commit in one
|
|
29
|
+
> transaction.
|
|
30
|
+
shop.oms -> bus: event shop.oms.order.OrderPlaced [verified]
|
|
31
|
+
payments.ledger -> psp-gateway: rpc psp.v2.Charges/Create as "Charges.Create (auth only)" [unresolved] @internal/ledger/adapter/psp/client.go:64
|
|
32
|
+
|
|
33
|
+
alt score below 40 #alt-risk
|
|
34
|
+
bus -> payments.ledger: event shop.oms.order.OrderPlaced
|
|
35
|
+
else score at or above 40
|
|
36
|
+
shop.oms -> bus: event shop.oms.order.OrderCancelled
|
|
37
|
+
stop
|
|
38
|
+
else
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
par OrderPlaced fan-out #par-placed
|
|
42
|
+
bus -> payments.ledger: event shop.oms.order.OrderPlaced
|
|
43
|
+
and
|
|
44
|
+
bus -> delivery.core: event shop.oms.order.OrderPlaced
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
loop outbox relay, every 200 ms until the batch is empty
|
|
48
|
+
shop.oms -> oms-db: SELECT ... FOR UPDATE SKIP LOCKED
|
|
49
|
+
end
|
|
50
|
+
`
|
|
51
|
+
|
|
52
|
+
func parsed(t *testing.T, src string) catalog.Flow {
|
|
53
|
+
t.Helper()
|
|
54
|
+
flow, errs := parseFlow("data/flows/order-accepted.flow.md", src)
|
|
55
|
+
if len(errs) > 0 {
|
|
56
|
+
t.Fatalf("errors: %s", strings.Join(errs, "\n"))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return flow
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func TestTheHeadIsReadIntoTheFlow(t *testing.T) {
|
|
63
|
+
flow := parsed(t, sample)
|
|
64
|
+
if flow.ID != "flow.order-accepted" || flow.Slug != "order-accepted" || flow.Name != "Order accepted" || flow.Owner != "shop" {
|
|
65
|
+
t.Errorf("flow = %+v", flow)
|
|
66
|
+
}
|
|
67
|
+
if flow.Source != "services/oms/test/integration/order_accepted_test.go" {
|
|
68
|
+
t.Errorf("source = %q", flow.Source)
|
|
69
|
+
}
|
|
70
|
+
want := "The narrow slice one integration test pins end to end: an order commits with its outbox row.\n\nEvery hop here is asserted."
|
|
71
|
+
if flow.Summary != want {
|
|
72
|
+
t.Errorf("summary = %q", flow.Summary)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Declared lanes come first, in the order written; the ones a step brings in
|
|
77
|
+
// - services by their id, the bus by its name - follow in order of first use.
|
|
78
|
+
func TestLanesAreDeclaredOrInferred(t *testing.T) {
|
|
79
|
+
flow := parsed(t, sample)
|
|
80
|
+
var ids []string
|
|
81
|
+
for _, p := range flow.Participants {
|
|
82
|
+
ids = append(ids, p.ID+"/"+string(p.Kind))
|
|
83
|
+
}
|
|
84
|
+
want := "oms-db/store psp-gateway/external customer/actor shop.oms/service bus/broker payments.ledger/service delivery.core/service"
|
|
85
|
+
if strings.Join(ids, " ") != want {
|
|
86
|
+
t.Errorf("lanes = %s", strings.Join(ids, " "))
|
|
87
|
+
}
|
|
88
|
+
if store := flow.Participants[0]; store.Label != "oms-db (postgres)" || store.Context == nil || *store.Context != "shop" {
|
|
89
|
+
t.Errorf("store lane = %+v", store)
|
|
90
|
+
}
|
|
91
|
+
if oms := flow.Participants[3]; oms.Context == nil || *oms.Context != "shop" {
|
|
92
|
+
t.Errorf("service lane = %+v", oms)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func TestAHopCarriesEverythingWrittenAfterIt(t *testing.T) {
|
|
97
|
+
flow := parsed(t, sample)
|
|
98
|
+
first := flow.Steps[0].(*catalog.Step)
|
|
99
|
+
if first.ID != "s1" || first.Kind != catalog.StepRPC || first.Label != "PlaceOrder" || first.Ref != "" || first.Status != catalog.StatusVerified || first.Line != "internal/oms/http/orders.go:40" {
|
|
100
|
+
t.Errorf("first = %+v", first)
|
|
101
|
+
}
|
|
102
|
+
second := flow.Steps[1].(*catalog.Step)
|
|
103
|
+
if second.ID != "a1" || second.Kind != catalog.StepCall || second.Label != "insertOrderAndOutboxRow" {
|
|
104
|
+
t.Errorf("second = %+v", second)
|
|
105
|
+
}
|
|
106
|
+
if second.Note != "The order row and the OrderPlaced outbox row commit in one transaction." {
|
|
107
|
+
t.Errorf("note = %q", second.Note)
|
|
108
|
+
}
|
|
109
|
+
third := flow.Steps[2].(*catalog.Step)
|
|
110
|
+
if third.ID != "s2" || third.Ref != "shop.oms.order.OrderPlaced" || third.Label != "OrderPlaced" || third.From != "shop.oms" || third.To != "bus" {
|
|
111
|
+
t.Errorf("third = %+v; an explicit id does not use up a number", third)
|
|
112
|
+
}
|
|
113
|
+
fourth := flow.Steps[3].(*catalog.Step)
|
|
114
|
+
if fourth.Ref != "psp.v2.Charges/Create" || fourth.Label != "Charges.Create (auth only)" || fourth.Status != catalog.StatusUnresolved {
|
|
115
|
+
t.Errorf("fourth = %+v", fourth)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func TestFramesCloseWithEnd(t *testing.T) {
|
|
120
|
+
flow := parsed(t, sample)
|
|
121
|
+
alt, ok := flow.Steps[4].(*catalog.Alt)
|
|
122
|
+
if !ok || alt.ID != "alt-risk" || len(alt.Branches) != 3 {
|
|
123
|
+
t.Fatalf("alt = %+v", flow.Steps[4])
|
|
124
|
+
}
|
|
125
|
+
if alt.Branches[0].Title != "score below 40" || alt.Branches[0].Terminal || len(alt.Branches[0].Steps) != 1 {
|
|
126
|
+
t.Errorf("branch 0 = %+v", alt.Branches[0])
|
|
127
|
+
}
|
|
128
|
+
if alt.Branches[1].Title != "score at or above 40" || !alt.Branches[1].Terminal {
|
|
129
|
+
t.Errorf("branch 1 = %+v; `stop` makes it terminal", alt.Branches[1])
|
|
130
|
+
}
|
|
131
|
+
if alt.Branches[2].Title != "otherwise" || len(alt.Branches[2].Steps) != 0 {
|
|
132
|
+
t.Errorf("branch 2 = %+v; a bare else is otherwise, and empty is allowed", alt.Branches[2])
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
par, ok := flow.Steps[5].(*catalog.Parallel)
|
|
136
|
+
if !ok || par.ID != "par-placed" || par.Title != "OrderPlaced fan-out" || len(par.Branches) != 2 {
|
|
137
|
+
t.Fatalf("par = %+v", flow.Steps[5])
|
|
138
|
+
}
|
|
139
|
+
loop, ok := flow.Steps[6].(*catalog.Loop)
|
|
140
|
+
if !ok || loop.ID != "loop8" || !strings.HasPrefix(loop.Title, "outbox relay") || len(loop.Steps) != 1 {
|
|
141
|
+
t.Fatalf("loop = %+v", flow.Steps[6])
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// An rpc with no interface behind it - a webhook arriving on a route - keeps
|
|
146
|
+
// the route as its label and has no ref to resolve.
|
|
147
|
+
func TestARouteIsALabelNotARef(t *testing.T) {
|
|
148
|
+
src := "# Webhook\nowner: payments\n\n## Participants\n- psp-gateway: external\n\n## Steps\npsp-gateway -> payments.ledger: rpc POST /webhooks/psp/v2 [verified]\npayments.ledger -> shop.oms: rpc shop.v1.Orders/GetOrder\n"
|
|
149
|
+
flow, errs := parseFlow("w.flow.md", src)
|
|
150
|
+
if len(errs) > 0 {
|
|
151
|
+
t.Fatal(errs)
|
|
152
|
+
}
|
|
153
|
+
first := flow.Steps[0].(*catalog.Step)
|
|
154
|
+
if first.Ref != "" || first.Label != "POST /webhooks/psp/v2" {
|
|
155
|
+
t.Errorf("first = %+v", first)
|
|
156
|
+
}
|
|
157
|
+
second := flow.Steps[1].(*catalog.Step)
|
|
158
|
+
if second.Ref != "shop.v1.Orders/GetOrder" || second.Label != "GetOrder" {
|
|
159
|
+
t.Errorf("second = %+v", second)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func TestTheFileIsTheSourceWhenNoneIsNamed(t *testing.T) {
|
|
164
|
+
src := "# Login\nowner: auth\n\n## Steps\nclient -> auth.auth: rpc login\n"
|
|
165
|
+
flow, errs := parseFlow("data/flows/login.flow.md", src)
|
|
166
|
+
if len(errs) > 0 {
|
|
167
|
+
t.Fatal(errs)
|
|
168
|
+
}
|
|
169
|
+
if flow.Source != "data/flows/login.flow.md" || flow.Slug != "login" {
|
|
170
|
+
t.Errorf("flow = %+v", flow)
|
|
171
|
+
}
|
|
172
|
+
if flow.Participants[0].Kind != catalog.ParticipantActor || flow.Participants[0].ID != "client" {
|
|
173
|
+
t.Errorf("client is an actor by name: %+v", flow.Participants)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Every mistake is named with its line, and none of them is silent.
|
|
178
|
+
func TestMistakesAreNamedWithTheirLine(t *testing.T) {
|
|
179
|
+
cases := []struct{ src, want string }{
|
|
180
|
+
{"# X\nowner: a\n\n## Steps\na.b -> c.d: hop\nalt when\n a.b -> c.d: x\n", "alt is never closed"},
|
|
181
|
+
{"# X\nowner: a\n\n## Steps\nalt when\n a.b -> c.d: x\nend\n", "needs an `else`"},
|
|
182
|
+
{"# X\nowner: a\n\n## Steps\nalt when\n a.b -> c.d: x\n stop\nelse\n a.b -> c.d: y\n stop\nend\n", "every branch of this alt stops"},
|
|
183
|
+
{"# X\nowner: a\n\n## Steps\na.b -> mystery: hop\n", `"mystery" is not declared`},
|
|
184
|
+
{"# X\nowner: a\n\n## Steps\na.b -> c.d: hop [maybe]\n", "not one of verified"},
|
|
185
|
+
{"# X\nowner: a\n\n## Steps\na.b -> c.d: hop #s1\na.b -> c.d: hop #s1\n", "used twice"},
|
|
186
|
+
{"# X\nowner: a\n\n## Steps\n> orphan\n", "has to follow the step"},
|
|
187
|
+
{"# X\n\n## Steps\na.b -> c.d: hop\n", "no owner"},
|
|
188
|
+
{"# X\nowner: a\n\n## Steps\nloop\n a.b -> c.d: hop\nend\n", "loop needs a title"},
|
|
189
|
+
{"# X\nowner: a\n\n## Steps\nalt when\n a.b -> c.d: x\n stop\n a.b -> c.d: y\nelse\nend\n", "nothing runs after `stop`"},
|
|
190
|
+
{"# X\nowner: a\n\n## Steps\na.b -> c.d: hop [verified] wat\n", "cannot read"},
|
|
191
|
+
}
|
|
192
|
+
for _, c := range cases {
|
|
193
|
+
_, errs := parseFlow("f.flow.md", c.src)
|
|
194
|
+
if len(errs) == 0 {
|
|
195
|
+
t.Errorf("%q: no error, want %q", c.src, c.want)
|
|
196
|
+
|
|
197
|
+
continue
|
|
198
|
+
}
|
|
199
|
+
joined := strings.Join(errs, "\n")
|
|
200
|
+
if !strings.Contains(joined, c.want) || !strings.Contains(joined, "f.flow.md:") {
|
|
201
|
+
t.Errorf("%q: errors = %q, want %q with a line", c.src, joined, c.want)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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-glossary",
|
|
15
|
+
Summary: "Reads a context's GLOSSARY.md - one paragraph per term, what it is and what it is not - into a catalog fragment.",
|
|
16
|
+
Phases: []string{plugin.PhaseExtract},
|
|
17
|
+
Options: optionsSchema,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"errors"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"regexp"
|
|
9
|
+
"sort"
|
|
10
|
+
"strings"
|
|
11
|
+
|
|
12
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
13
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
var contextSlug = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
|
|
17
|
+
|
|
18
|
+
// extract reads every glossary the options name and answers with one fragment
|
|
19
|
+
// holding their terms.
|
|
20
|
+
//
|
|
21
|
+
// A file that cannot be parsed fails the whole run rather than being left out:
|
|
22
|
+
// a fragment silently missing half a vocabulary is a site where a word simply
|
|
23
|
+
// has no meaning, with nothing on the page to say a file was skipped and why.
|
|
24
|
+
// What is merely incomplete - an entry that never says what the term is not,
|
|
25
|
+
// a glossary that drifted out of alphabetical order - comes back as warnings,
|
|
26
|
+
// which the host prints and nobody has to act on today.
|
|
27
|
+
func extract(in plugin.Input, opts Options) (plugin.Response, error) {
|
|
28
|
+
b := &plugin.Builder{}
|
|
29
|
+
|
|
30
|
+
context := opts.Context
|
|
31
|
+
if context == "" {
|
|
32
|
+
context = filepath.Base(in.Root)
|
|
33
|
+
}
|
|
34
|
+
if !contextSlug.MatchString(context) {
|
|
35
|
+
return plugin.Response{}, errors.New(context + " is not a context slug: a term's id is the context and the term, so the context has to be one word the way the estate spells it")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
patterns := opts.Files
|
|
39
|
+
if len(patterns) == 0 {
|
|
40
|
+
patterns = []string{"GLOSSARY.md"}
|
|
41
|
+
}
|
|
42
|
+
var files []string
|
|
43
|
+
seen := map[string]bool{}
|
|
44
|
+
for _, pattern := range patterns {
|
|
45
|
+
matches, err := filepath.Glob(filepath.Join(in.Root, pattern))
|
|
46
|
+
if err != nil {
|
|
47
|
+
return plugin.Response{}, err
|
|
48
|
+
}
|
|
49
|
+
for _, match := range matches {
|
|
50
|
+
if !seen[match] {
|
|
51
|
+
seen[match] = true
|
|
52
|
+
files = append(files, match)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
sort.Strings(files)
|
|
57
|
+
if len(files) == 0 {
|
|
58
|
+
b.Warn(in.Root, "no glossary matched "+strings.Join(patterns, ", ")+"; the fragment holds no terms")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
terms := []catalog.Term{}
|
|
62
|
+
ids := map[string]string{}
|
|
63
|
+
var problems []string
|
|
64
|
+
for _, file := range files {
|
|
65
|
+
src, err := os.ReadFile(file)
|
|
66
|
+
if err != nil {
|
|
67
|
+
return plugin.Response{}, err
|
|
68
|
+
}
|
|
69
|
+
rel := filepath.ToSlash(file)
|
|
70
|
+
parsed, warns, errs := parseGlossary(rel, context, string(src))
|
|
71
|
+
if len(errs) > 0 {
|
|
72
|
+
problems = append(problems, errs...)
|
|
73
|
+
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
for _, warn := range warns {
|
|
77
|
+
b.Warn(rel, strings.TrimPrefix(warn, rel+": "))
|
|
78
|
+
}
|
|
79
|
+
for _, term := range parsed {
|
|
80
|
+
// Two files in one context defining one word is the failure the
|
|
81
|
+
// glossary exists to prevent, said in the glossaries themselves.
|
|
82
|
+
if other, taken := ids[term.ID]; taken {
|
|
83
|
+
problems = append(problems, term.Source+": "+term.Name+" is already defined in "+other)
|
|
84
|
+
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
ids[term.ID] = term.Source
|
|
88
|
+
terms = append(terms, term)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if len(problems) > 0 {
|
|
92
|
+
return plugin.Response{}, errors.New(strings.Join(problems, "\n"))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fragment := catalog.Catalog{
|
|
96
|
+
GeneratedAt: in.GeneratedAt,
|
|
97
|
+
Commit: in.Commit,
|
|
98
|
+
Contexts: []catalog.BoundedContext{},
|
|
99
|
+
Defs: map[string]catalog.TypeDef{},
|
|
100
|
+
Flows: []catalog.Flow{},
|
|
101
|
+
Adrs: []catalog.Adr{},
|
|
102
|
+
Terms: terms,
|
|
103
|
+
}
|
|
104
|
+
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
105
|
+
if err != nil {
|
|
106
|
+
return plugin.Response{}, err
|
|
107
|
+
}
|
|
108
|
+
out := opts.Out
|
|
109
|
+
if out == "" {
|
|
110
|
+
out = "glossary.json"
|
|
111
|
+
}
|
|
112
|
+
b.File(out, string(encoded)+"\n")
|
|
113
|
+
|
|
114
|
+
return b.Response(), nil
|
|
115
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// The mapping: glossaries in a tree, a catalog fragment out.
|
|
4
|
+
//
|
|
5
|
+
// Shaped like plugins/extract-adr/extract_test.go, which is the house style
|
|
6
|
+
// for an extractor test - one behaviour per test, and a whole-fragment golden
|
|
7
|
+
// underneath so an unintended field change is a diff rather than an assertion
|
|
8
|
+
// nobody thought to write.
|
|
9
|
+
|
|
10
|
+
import (
|
|
11
|
+
"encoding/json"
|
|
12
|
+
"flag"
|
|
13
|
+
"os"
|
|
14
|
+
"strconv"
|
|
15
|
+
"strings"
|
|
16
|
+
"testing"
|
|
17
|
+
|
|
18
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
19
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
var update = flag.Bool("update", false, "rewrite the golden fragment instead of comparing against it")
|
|
23
|
+
|
|
24
|
+
const goldenPath = "testdata/golden/glossary.json"
|
|
25
|
+
|
|
26
|
+
func input(root string) plugin.Input {
|
|
27
|
+
// The stamp comes from the host, which derives it from git. A plugin that
|
|
28
|
+
// read a clock would write a different file every run.
|
|
29
|
+
return plugin.Input{
|
|
30
|
+
Root: root,
|
|
31
|
+
Commit: "abc1234",
|
|
32
|
+
GeneratedAt: "2024-01-01T00:00:00Z",
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func response(t *testing.T) plugin.Response {
|
|
37
|
+
t.Helper()
|
|
38
|
+
|
|
39
|
+
resp, err := extract(input("testdata/estate"), Options{Context: "auth"})
|
|
40
|
+
if err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return resp
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func fragment(t *testing.T) catalog.Catalog {
|
|
48
|
+
t.Helper()
|
|
49
|
+
|
|
50
|
+
var cat catalog.Catalog
|
|
51
|
+
if err := json.Unmarshal([]byte(response(t).Files[0].Contents), &cat); err != nil {
|
|
52
|
+
t.Fatal(err)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return cat
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func TestEveryTermInTheTreeIsRead(t *testing.T) {
|
|
59
|
+
var ids []string
|
|
60
|
+
for _, term := range fragment(t).Terms {
|
|
61
|
+
ids = append(ids, term.ID)
|
|
62
|
+
}
|
|
63
|
+
if strings.Join(ids, " ") != "auth.email-address auth.session auth.version" {
|
|
64
|
+
t.Errorf("ids = %v", ids)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The file sits beside a service and the words in it belong to a context, so
|
|
69
|
+
// the directory is the wrong answer whenever a context holds more than one
|
|
70
|
+
// service - which is the case this option exists for.
|
|
71
|
+
func TestTheContextIsAskedForRatherThanDerived(t *testing.T) {
|
|
72
|
+
resp, err := extract(input("testdata/estate"), Options{})
|
|
73
|
+
if err != nil {
|
|
74
|
+
t.Fatal(err)
|
|
75
|
+
}
|
|
76
|
+
var cat catalog.Catalog
|
|
77
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &cat); err != nil {
|
|
78
|
+
t.Fatal(err)
|
|
79
|
+
}
|
|
80
|
+
if got := cat.Terms[0].ID; got != "estate.email-address" {
|
|
81
|
+
t.Errorf("without the option the id is %q, want the directory's name", got)
|
|
82
|
+
}
|
|
83
|
+
if got := fragment(t).Terms[0].ID; got != "auth.email-address" {
|
|
84
|
+
t.Errorf("with the option the id is %q", got)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// A context that is not a slug would build ids nothing else in the estate can
|
|
89
|
+
// match, and the term would simply never be found again.
|
|
90
|
+
func TestAContextThatIsNotASlugIsRefused(t *testing.T) {
|
|
91
|
+
_, err := extract(input("testdata/estate"), Options{Context: "shop.oms"})
|
|
92
|
+
if err == nil {
|
|
93
|
+
t.Fatal("a dotted context produced a fragment")
|
|
94
|
+
}
|
|
95
|
+
if !strings.Contains(err.Error(), "is not a context slug") {
|
|
96
|
+
t.Errorf("refusal is %q", err)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The fragment is one source among several, so it carries the empty lists of
|
|
101
|
+
// everything it says nothing about rather than leaving the merge to guess.
|
|
102
|
+
func TestTheFragmentSaysNothingItDidNotRead(t *testing.T) {
|
|
103
|
+
cat := fragment(t)
|
|
104
|
+
if len(cat.Contexts) != 0 || len(cat.Flows) != 0 || len(cat.Defs) != 0 || len(cat.Adrs) != 0 {
|
|
105
|
+
t.Errorf("the fragment claims more than its terms: %+v", cat)
|
|
106
|
+
}
|
|
107
|
+
if cat.Commit != "abc1234" || cat.GeneratedAt != "2024-01-01T00:00:00Z" {
|
|
108
|
+
t.Errorf("stamp = %q %q", cat.Commit, cat.GeneratedAt)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func TestTheFileIsNamedByTheOptions(t *testing.T) {
|
|
113
|
+
resp, err := extract(input("testdata/estate"), Options{Context: "auth", Out: "words.json"})
|
|
114
|
+
if err != nil {
|
|
115
|
+
t.Fatal(err)
|
|
116
|
+
}
|
|
117
|
+
if resp.Files[0].Name != "words.json" {
|
|
118
|
+
t.Errorf("name = %q", resp.Files[0].Name)
|
|
119
|
+
}
|
|
120
|
+
if response(t).Files[0].Name != "glossary.json" {
|
|
121
|
+
t.Errorf("the default is not glossary.json")
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// A root with no glossary is worth saying out loud. A step pointed at the
|
|
126
|
+
// wrong directory answers with an empty fragment either way, and the warning
|
|
127
|
+
// is the only thing that tells the two apart.
|
|
128
|
+
func TestARootWithNoGlossaryWarns(t *testing.T) {
|
|
129
|
+
resp, err := extract(input("testdata"), Options{Context: "auth"})
|
|
130
|
+
if err != nil {
|
|
131
|
+
t.Fatal(err)
|
|
132
|
+
}
|
|
133
|
+
if !warnedAbout(resp, "no glossary matched") {
|
|
134
|
+
t.Errorf("diagnostics = %+v", resp.Warnings())
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Every rule the format has is checked here, where the file that broke it can
|
|
139
|
+
// be named. A fragment written half-valid is a word with no meaning on a page
|
|
140
|
+
// later, with the reason a long way from its cause.
|
|
141
|
+
func TestABrokenGlossaryIsRefusedWholeRatherThanWrittenHalf(t *testing.T) {
|
|
142
|
+
resp, err := extract(input("testdata/broken"), Options{Context: "shop"})
|
|
143
|
+
if err == nil {
|
|
144
|
+
t.Fatal("the broken tree produced a fragment")
|
|
145
|
+
}
|
|
146
|
+
if len(resp.Files) != 0 {
|
|
147
|
+
t.Errorf("files were named anyway: %+v", resp.Files)
|
|
148
|
+
}
|
|
149
|
+
if !strings.Contains(err.Error(), "prose") {
|
|
150
|
+
t.Errorf("refusal is %q", err)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// One context, one vocabulary: two files defining the same word is the failure
|
|
155
|
+
// the glossary is written to prevent, and it is only visible from up here.
|
|
156
|
+
func TestTheSameWordInTwoFilesIsRefused(t *testing.T) {
|
|
157
|
+
_, err := extract(input("testdata/twice"), Options{Context: "shop", Files: []string{"*.GLOSSARY.md"}})
|
|
158
|
+
if err == nil {
|
|
159
|
+
t.Fatal("two definitions of one word produced a fragment")
|
|
160
|
+
}
|
|
161
|
+
if !strings.Contains(err.Error(), "Order is already defined in") {
|
|
162
|
+
t.Errorf("refusal is %q", err)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// The fragment is committed and compared by gen:check. A map iterated in Go's
|
|
167
|
+
// order would rewrite the file every run and turn every build into a diff.
|
|
168
|
+
func TestOutputIsByteIdentical(t *testing.T) {
|
|
169
|
+
first := response(t).Files[0].Contents
|
|
170
|
+
for i := 0; i < 5; i++ {
|
|
171
|
+
if again := response(t).Files[0].Contents; again != first {
|
|
172
|
+
t.Fatal("two runs over the same tree produced different fragments")
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// The whole fragment, so a field that changes shape shows up as a diff rather
|
|
178
|
+
// than slipping past every assertion above.
|
|
179
|
+
func TestGoldenFragment(t *testing.T) {
|
|
180
|
+
got := response(t).Files[0].Contents
|
|
181
|
+
|
|
182
|
+
if *update {
|
|
183
|
+
if err := os.WriteFile(goldenPath, []byte(got), 0o644); err != nil {
|
|
184
|
+
t.Fatal(err)
|
|
185
|
+
}
|
|
186
|
+
t.Log("golden fragment rewritten")
|
|
187
|
+
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
want, err := os.ReadFile(goldenPath)
|
|
192
|
+
if err != nil {
|
|
193
|
+
t.Fatalf("reading the golden fragment: %v (run `go test ./plugins/extract-glossary -update`)", err)
|
|
194
|
+
}
|
|
195
|
+
if string(want) != got {
|
|
196
|
+
t.Errorf("the fragment differs from the golden file\n%s", firstDifference(string(want), got))
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
func warnedAbout(resp plugin.Response, substring string) bool {
|
|
201
|
+
for _, d := range resp.Warnings() {
|
|
202
|
+
if strings.Contains(d.Message, substring) {
|
|
203
|
+
return true
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return false
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
func firstDifference(want, got string) string {
|
|
211
|
+
a := strings.Split(want, "\n")
|
|
212
|
+
b := strings.Split(got, "\n")
|
|
213
|
+
for i := 0; i < len(a) && i < len(b); i++ {
|
|
214
|
+
if a[i] != b[i] {
|
|
215
|
+
return "line " + strconv.Itoa(i+1) + ":\n want: " + a[i] + "\n got: " + b[i]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return "want " + strconv.Itoa(len(a)) + " lines, got " + strconv.Itoa(len(b))
|
|
220
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Command extract-glossary reads the vocabulary a bounded context speaks, as
|
|
2
|
+
// the GLOSSARY.md it is already written in, and answers with a catalog
|
|
3
|
+
// fragment.
|
|
4
|
+
//
|
|
5
|
+
// The glossary is the one file in a service that is written for a person and
|
|
6
|
+
// read by everyone: it says what a word means inside the boundary, and what it
|
|
7
|
+
// does NOT mean, which is where the confusion it was written to settle lives.
|
|
8
|
+
// Nothing generates it and nothing should - a definition is a decision about
|
|
9
|
+
// language, not a fact about code - so this plugin only reads.
|
|
10
|
+
//
|
|
11
|
+
// What it refuses is as much the point as what it parses. A table, a bullet
|
|
12
|
+
// list, a heading per term: all of them hold the same words, and every one of
|
|
13
|
+
// them makes the negative half of an entry either impossible to write or
|
|
14
|
+
// impossible to find. One shape, held to here, is what lets a page put "is"
|
|
15
|
+
// and "is not" on two lines everywhere in the estate. The format is described
|
|
16
|
+
// in plugins/README.md and held to by parse_test.go.
|
|
17
|
+
package main
|
|
18
|
+
|
|
19
|
+
import (
|
|
20
|
+
"fmt"
|
|
21
|
+
"io"
|
|
22
|
+
"os"
|
|
23
|
+
|
|
24
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Options are what the manifest tells the extractor.
|
|
28
|
+
type Options struct {
|
|
29
|
+
// Context is the bounded context whose vocabulary this is. It is asked for
|
|
30
|
+
// rather than derived because the file sits beside a SERVICE - shop.oms
|
|
31
|
+
// keeps one, and the words in it belong to shop. Left out, the name of the
|
|
32
|
+
// input directory, which is right only when the two coincide.
|
|
33
|
+
Context string `json:"context,omitempty"`
|
|
34
|
+
|
|
35
|
+
// Files are the glossaries to read, as globs relative to the input root.
|
|
36
|
+
// Left out, `GLOSSARY.md` at the root of it, which is where the layout
|
|
37
|
+
// rules put it - beside the README.
|
|
38
|
+
Files []string `json:"files,omitempty"`
|
|
39
|
+
|
|
40
|
+
// Out names the fragment file.
|
|
41
|
+
Out string `json:"out,omitempty"`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func main() {
|
|
45
|
+
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
46
|
+
fmt.Fprintln(os.Stderr, "portolan-extract-glossary:", err)
|
|
47
|
+
os.Exit(1)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func run(stdin io.Reader, stdout io.Writer) error {
|
|
52
|
+
return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
|
|
53
|
+
if req.Input.Root == "" {
|
|
54
|
+
return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return extract(req.Input, opts)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"properties": {
|
|
5
|
+
"context": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "Slug of the bounded context whose vocabulary this is. A glossary sits beside a service, and the words in it belong to the context that service is in, so shop/oms/GLOSSARY.md is read as shop. Left out, the name of the input directory.",
|
|
8
|
+
"examples": ["auth", "shop"]
|
|
9
|
+
},
|
|
10
|
+
"files": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": { "type": "string" },
|
|
13
|
+
"description": "The glossaries to read, as globs relative to the input root. Left out, GLOSSARY.md at the root of it.",
|
|
14
|
+
"examples": [["GLOSSARY.md"], ["docs/GLOSSARY.md"]]
|
|
15
|
+
},
|
|
16
|
+
"out": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Name of the fragment file, inside the step's output directory. Left out, glossary.json."
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|