@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,50 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"reflect"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// The note names the table it stands above, and only that one. A bare slug is
|
|
9
|
+
// an aggregate of the store's owner; a dotted name is somebody's full id.
|
|
10
|
+
func TestAggregateNoteBindsToTheNextCreateTable(t *testing.T) {
|
|
11
|
+
got := readProjected(`
|
|
12
|
+
-- Planned stops, rebuilt from RoutePlanned.
|
|
13
|
+
-- aggregate: shop.oms.order
|
|
14
|
+
CREATE TABLE route_stops (
|
|
15
|
+
route_id text NOT NULL
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
-- aggregate: price_list
|
|
19
|
+
CREATE TABLE IF NOT EXISTS board.prices (
|
|
20
|
+
id text PRIMARY KEY
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE seen (
|
|
24
|
+
id text PRIMARY KEY
|
|
25
|
+
);`, "shop.delivery")
|
|
26
|
+
|
|
27
|
+
want := map[string]string{
|
|
28
|
+
"route_stops": "shop.oms.order",
|
|
29
|
+
"prices": "shop.delivery.price-list",
|
|
30
|
+
}
|
|
31
|
+
if !reflect.DeepEqual(got, want) {
|
|
32
|
+
t.Errorf("projected = %v, want %v", got, want)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// A note that a statement gets in front of was not about the table after it,
|
|
37
|
+
// and a note nothing follows says nothing.
|
|
38
|
+
func TestAggregateNoteDoesNotReachPastAStatement(t *testing.T) {
|
|
39
|
+
got := readProjected(`
|
|
40
|
+
-- aggregate: shop.oms.order
|
|
41
|
+
CREATE INDEX route_stops_by_route ON route_stops (route_id);
|
|
42
|
+
CREATE TABLE route_stops_copy (
|
|
43
|
+
route_id text NOT NULL
|
|
44
|
+
);
|
|
45
|
+
-- aggregate: shop.oms.order`, "shop.delivery")
|
|
46
|
+
|
|
47
|
+
if len(got) != 0 {
|
|
48
|
+
t.Errorf("no table should carry the note, got %v", got)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/parser"
|
|
5
|
+
"go/token"
|
|
6
|
+
"os"
|
|
7
|
+
"path"
|
|
8
|
+
"path/filepath"
|
|
9
|
+
"regexp"
|
|
10
|
+
"sort"
|
|
11
|
+
"strings"
|
|
12
|
+
|
|
13
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
14
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// readStore builds one store out of every migration in the infrastructure
|
|
18
|
+
// layer: the repository packages, and the projector packages beside them.
|
|
19
|
+
//
|
|
20
|
+
// One store, not one per aggregate: the migrations are numbered inside their
|
|
21
|
+
// own package and each keeps its own schema_migrations table, but they are all
|
|
22
|
+
// applied to the same database. The numbering is about who waits for whom, not
|
|
23
|
+
// about where the rows live.
|
|
24
|
+
func readStore(root string, layout storageLayout, storeID, owner string, b *plugin.Builder) ([]catalog.Table, []catalog.View) {
|
|
25
|
+
tables := []catalog.Table{}
|
|
26
|
+
views := []catalog.View{}
|
|
27
|
+
|
|
28
|
+
for _, repository := range layout.repositories {
|
|
29
|
+
aggregate := repository.name
|
|
30
|
+
dir := repository.migrations
|
|
31
|
+
|
|
32
|
+
state, copies, _, ok := readMigrations(root, dir, storeID, owner, b)
|
|
33
|
+
if !ok {
|
|
34
|
+
// A repository package with no migrations of its own is normal:
|
|
35
|
+
// not every adapter keeps rows.
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The directory names the aggregate, and an id spells it the way every
|
|
40
|
+
// extractor spells one: price_list is price-list.
|
|
41
|
+
aggregateID := owner + "." + slug(aggregate)
|
|
42
|
+
// Which column carries which field, read from the statements that
|
|
43
|
+
// write the rows rather than from the column names.
|
|
44
|
+
mapped := readMaps(root, repository.dir, aggregate, b)
|
|
45
|
+
for _, more := range []map[string]map[string]string{
|
|
46
|
+
readMapsTS(root, repository.dir, aggregate, b),
|
|
47
|
+
readMapsRust(root, repository.dir, aggregate, b),
|
|
48
|
+
readMapsJava(root, repository.dir, aggregate, b),
|
|
49
|
+
} {
|
|
50
|
+
for table, columns := range more {
|
|
51
|
+
if _, ok := mapped[table]; !ok {
|
|
52
|
+
mapped[table] = columns
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
first := true
|
|
58
|
+
for _, relation := range state.relations {
|
|
59
|
+
table := finishTable(relation, state, storeID)
|
|
60
|
+
// An outbox holds messages on their way out, not the
|
|
61
|
+
// aggregate: it is created beside the aggregate because the
|
|
62
|
+
// repository writes both in one transaction, and that is all
|
|
63
|
+
// the layout says about it.
|
|
64
|
+
if isOutbox(lastNamePart(table.Name)) {
|
|
65
|
+
table.Role = catalog.TableRoleOutbox
|
|
66
|
+
tables = append(tables, table)
|
|
67
|
+
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The layout is the claim: these rows exist because this
|
|
72
|
+
// aggregate exists, and its schema lives beside the code that
|
|
73
|
+
// reads it.
|
|
74
|
+
table.Persists = &catalog.Persists{Aggregate: aggregateID}
|
|
75
|
+
|
|
76
|
+
mapping := forTable(mapped, table.Name)
|
|
77
|
+
for i := range table.Columns {
|
|
78
|
+
if field, ok := mapping[table.Columns[i].Name]; ok {
|
|
79
|
+
table.Columns[i].Maps = field
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
applyCopies(&table, copies)
|
|
83
|
+
|
|
84
|
+
// The first table an aggregate creates holds the aggregate
|
|
85
|
+
// itself; anything it creates afterwards hangs off it.
|
|
86
|
+
if first {
|
|
87
|
+
table.Role = catalog.TableRoleAggregateRoot
|
|
88
|
+
first = false
|
|
89
|
+
} else {
|
|
90
|
+
table.Role = catalog.TableRoleChild
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
tables = append(tables, table)
|
|
94
|
+
}
|
|
95
|
+
for _, declared := range state.views {
|
|
96
|
+
views = append(views, declared.asCatalog(storeID, declared.source, tables))
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// A projector package holds one projection: rows assembled from events,
|
|
101
|
+
// kept by a subscriber that writes and nothing else. The layout says the
|
|
102
|
+
// role. It does not say whose rows these are a picture of - a projection
|
|
103
|
+
// of another service's aggregate lives here too, and its name is not in
|
|
104
|
+
// any directory of this tree - so the link is taken from the migration
|
|
105
|
+
// when it is written there (`-- aggregate:`) and left out when it is not.
|
|
106
|
+
for _, projector := range layout.projectors {
|
|
107
|
+
dir := projector.migrations
|
|
108
|
+
|
|
109
|
+
state, copies, projected, ok := readMigrations(root, dir, storeID, owner, b)
|
|
110
|
+
if !ok {
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for _, relation := range state.relations {
|
|
115
|
+
table := finishTable(relation, state, storeID)
|
|
116
|
+
table.Role = catalog.TableRoleProjection
|
|
117
|
+
if aggregate := forTable(projected, table.Name); aggregate != "" {
|
|
118
|
+
table.Persists = &catalog.Persists{Aggregate: aggregate}
|
|
119
|
+
}
|
|
120
|
+
// No `maps`: the upsert in a projector writes what an event
|
|
121
|
+
// carries, and an event's field is not a field of the aggregate
|
|
122
|
+
// the way a repository's insert argument is. Where a value came
|
|
123
|
+
// from is said by `-- from:`, which is read.
|
|
124
|
+
applyCopies(&table, copies)
|
|
125
|
+
|
|
126
|
+
tables = append(tables, table)
|
|
127
|
+
}
|
|
128
|
+
for _, declared := range state.views {
|
|
129
|
+
views = append(views, declared.asCatalog(storeID, declared.source, tables))
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// A view is read where it is written, which may be before the tables it
|
|
134
|
+
// reads are: the second pass is what lets a column take its type from the
|
|
135
|
+
// column it comes from wherever that was created.
|
|
136
|
+
for i := range views {
|
|
137
|
+
views[i] = resolveView(views[i], storeID, tables)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return tables, views
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// readMigrations applies every up migration under dir, in name order, and
|
|
144
|
+
// reads the comments the grammar drops: which columns are copies (`-- from:`)
|
|
145
|
+
// and which aggregate a table is a picture of (`-- aggregate:`). ok is false
|
|
146
|
+
// when there is no such directory.
|
|
147
|
+
func readMigrations(root, dir, storeID, owner string, b *plugin.Builder) (*ddlState, map[string]map[string][]string, map[string]string, bool) {
|
|
148
|
+
files, err := os.ReadDir(filepath.Join(root, filepath.FromSlash(dir)))
|
|
149
|
+
if err != nil {
|
|
150
|
+
return nil, nil, nil, false
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
names := make([]string, 0, len(files))
|
|
154
|
+
for _, file := range files {
|
|
155
|
+
// Only the up direction describes the schema. A down migration
|
|
156
|
+
// says how to lose it; a migration with no direction in its name
|
|
157
|
+
// only goes up.
|
|
158
|
+
if !file.IsDir() && strings.HasSuffix(file.Name(), ".sql") && !strings.HasSuffix(file.Name(), ".down.sql") {
|
|
159
|
+
names = append(names, file.Name())
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Applied in name order, so read in name order: a later migration is
|
|
163
|
+
// allowed to know about an earlier one.
|
|
164
|
+
sort.Strings(names)
|
|
165
|
+
|
|
166
|
+
state := newDDLState()
|
|
167
|
+
copies := map[string]map[string][]string{}
|
|
168
|
+
projected := map[string]string{}
|
|
169
|
+
|
|
170
|
+
for _, name := range names {
|
|
171
|
+
source := path.Join(dir, name)
|
|
172
|
+
|
|
173
|
+
sql, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(source)))
|
|
174
|
+
if err != nil {
|
|
175
|
+
b.Warn(storeID, source+" could not be read: "+err.Error())
|
|
176
|
+
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for table, columns := range readCopies(string(sql), storeID) {
|
|
181
|
+
if copies[table] == nil {
|
|
182
|
+
copies[table] = map[string][]string{}
|
|
183
|
+
}
|
|
184
|
+
for column, from := range columns {
|
|
185
|
+
copies[table][column] = from
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for table, aggregate := range readProjected(string(sql), owner) {
|
|
189
|
+
projected[table] = aggregate
|
|
190
|
+
}
|
|
191
|
+
unread, err := state.apply(string(sql), source)
|
|
192
|
+
if err != nil {
|
|
193
|
+
b.Warn(storeID, "could not parse "+source+": "+err.Error())
|
|
194
|
+
|
|
195
|
+
continue
|
|
196
|
+
}
|
|
197
|
+
for _, note := range unread {
|
|
198
|
+
b.Warn(storeID, source+": "+note)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return state, copies, projected, true
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// finishTable gives a relation its place in the catalog: an id under the
|
|
206
|
+
// store, its indexes, and its column types spelled the way the migration
|
|
207
|
+
// spelled them.
|
|
208
|
+
func finishTable(relation relation, state *ddlState, storeID string) catalog.Table {
|
|
209
|
+
table := relation.table
|
|
210
|
+
table.ID = storeID + "." + table.Name
|
|
211
|
+
table.Indexes = relation.indexes
|
|
212
|
+
for i := range table.Columns {
|
|
213
|
+
table.Columns[i].Type = state.renderType(table.Columns[i].Type)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return table
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// applyCopies writes onto each column where its value came from, when the
|
|
220
|
+
// migration says. A table cannot show a copy the way a view shows a select,
|
|
221
|
+
// so the copy is declared beside the column it lands in.
|
|
222
|
+
func applyCopies(table *catalog.Table, copies map[string]map[string][]string) {
|
|
223
|
+
copying := forTable(copies, table.Name)
|
|
224
|
+
for i := range table.Columns {
|
|
225
|
+
if from, ok := copying[table.Columns[i].Name]; ok {
|
|
226
|
+
table.Columns[i].From = from
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// forTable looks a table up by the name the migration used, then by its
|
|
232
|
+
// unqualified name: a comment says `orders`, the grammar says `sales.orders`.
|
|
233
|
+
func forTable[T any](byName map[string]T, name string) T {
|
|
234
|
+
if held, ok := byName[name]; ok {
|
|
235
|
+
return held
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return byName[lastNamePart(name)]
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// resolveView fills in what a view could not know when it was read: the type,
|
|
242
|
+
// nullability and mapping of every column that comes from exactly one place.
|
|
243
|
+
func resolveView(held catalog.View, storeID string, tables []catalog.Table) catalog.View {
|
|
244
|
+
for i := range held.Columns {
|
|
245
|
+
column := &held.Columns[i]
|
|
246
|
+
if column.Type != "" || len(column.From) != 1 {
|
|
247
|
+
continue
|
|
248
|
+
}
|
|
249
|
+
at := strings.LastIndex(column.From[0], ".")
|
|
250
|
+
if at < 0 {
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
if source := columnOf(tables, column.From[0][:at], column.From[0][at+1:]); source != nil {
|
|
254
|
+
column.Type = source.Type
|
|
255
|
+
column.Nullable = source.Nullable
|
|
256
|
+
column.Maps = source.Maps
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if held.Persists == nil {
|
|
260
|
+
persists := ""
|
|
261
|
+
for _, read := range held.Reads {
|
|
262
|
+
table := tableOf(tables, read)
|
|
263
|
+
if table == nil || table.Persists == nil {
|
|
264
|
+
continue
|
|
265
|
+
}
|
|
266
|
+
if persists == "" {
|
|
267
|
+
persists = table.Persists.Aggregate
|
|
268
|
+
|
|
269
|
+
continue
|
|
270
|
+
}
|
|
271
|
+
if persists != table.Persists.Aggregate {
|
|
272
|
+
persists = ""
|
|
273
|
+
|
|
274
|
+
break
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if persists != "" {
|
|
278
|
+
held.Persists = &catalog.Persists{Aggregate: persists}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return held
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// isOutbox says whether a table is the outbox by its name, which is the one
|
|
286
|
+
// convention every outbox library shares.
|
|
287
|
+
func isOutbox(name string) bool {
|
|
288
|
+
return name == "outbox" || strings.HasSuffix(name, "_outbox")
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// resolveForeignKeys turns the table names the grammar gives into table ids the
|
|
292
|
+
// catalog can follow. A key pointing outside this store keeps the raw name and
|
|
293
|
+
// is reported: it crosses a boundary this extractor cannot see the far side of.
|
|
294
|
+
func resolveForeignKeys(storeID string, tables []catalog.Table, b *plugin.Builder) {
|
|
295
|
+
known := map[string]bool{}
|
|
296
|
+
for i := range tables {
|
|
297
|
+
known[tables[i].Name] = true
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
for i := range tables {
|
|
301
|
+
for j := range tables[i].Columns {
|
|
302
|
+
fk := tables[i].Columns[j].FK
|
|
303
|
+
if fk == nil {
|
|
304
|
+
continue
|
|
305
|
+
}
|
|
306
|
+
if !known[fk.Table] {
|
|
307
|
+
b.Warn(tables[i].ID, "column "+tables[i].Columns[j].Name+" references "+fk.Table+", which no migration here creates")
|
|
308
|
+
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
fk.Table = storeID + "." + fk.Table
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// foreignSchemas reports migrations applied from outside this module.
|
|
317
|
+
//
|
|
318
|
+
// A service can bring a table with a dependency - an outbox published by an
|
|
319
|
+
// SDK, say - and its DDL is then in that module rather than in this tree. The
|
|
320
|
+
// table is real, the catalog cannot describe it, and silence would read as
|
|
321
|
+
// "there is no such table".
|
|
322
|
+
//
|
|
323
|
+
// What is checked is the IMPORT PATH, never the name a file refers to the
|
|
324
|
+
// package by. `userrepo.Migrations` and `sdkoutbox.Migrations` look equally
|
|
325
|
+
// foreign as identifiers, and only the path says that one of them is the
|
|
326
|
+
// repository package next door.
|
|
327
|
+
func foreignSchemas(root, module string, b *plugin.Builder, storeID string) {
|
|
328
|
+
if module == "" {
|
|
329
|
+
return
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
reported := map[string]bool{}
|
|
333
|
+
fset := token.NewFileSet()
|
|
334
|
+
|
|
335
|
+
_ = filepath.WalkDir(filepath.Join(root, "internal"), func(p string, entry os.DirEntry, err error) error {
|
|
336
|
+
if err != nil || entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
|
|
337
|
+
return nil
|
|
338
|
+
}
|
|
339
|
+
if strings.HasSuffix(entry.Name(), "_test.go") {
|
|
340
|
+
// A test standing up its own schema says nothing about what the
|
|
341
|
+
// service deploys.
|
|
342
|
+
return nil
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
file, err := parser.ParseFile(fset, p, nil, parser.ImportsOnly|parser.SkipObjectResolution)
|
|
346
|
+
if err != nil {
|
|
347
|
+
return nil
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
paths := map[string]string{}
|
|
351
|
+
for _, spec := range file.Imports {
|
|
352
|
+
importPath := strings.Trim(spec.Path.Value, `"`)
|
|
353
|
+
name := path.Base(importPath)
|
|
354
|
+
if spec.Name != nil {
|
|
355
|
+
name = spec.Name.Name
|
|
356
|
+
}
|
|
357
|
+
paths[name] = importPath
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Imports only got parsed above, so the bodies are gone; the file is
|
|
361
|
+
// read again for the one selector that matters.
|
|
362
|
+
source, err := os.ReadFile(p)
|
|
363
|
+
if err != nil {
|
|
364
|
+
return nil
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for _, match := range migrationsRef.FindAllStringSubmatch(string(source), -1) {
|
|
368
|
+
importPath, known := paths[match[1]]
|
|
369
|
+
if !known || reported[importPath] {
|
|
370
|
+
continue
|
|
371
|
+
}
|
|
372
|
+
if strings.HasPrefix(importPath, module) {
|
|
373
|
+
continue
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
reported[importPath] = true
|
|
377
|
+
b.Warn(storeID, "migrations are applied from "+importPath+", whose schema is not in this tree; the tables it creates are missing from this store")
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return nil
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// migrationsRef finds `<pkg>.Migrations`, which is how a migration set is
|
|
385
|
+
// handed to the migrator. A regular expression rather than a walk of the
|
|
386
|
+
// bodies: this produces a diagnostic and never a fact, and the day it misses
|
|
387
|
+
// one nothing in the catalog is wrong - there is only one fewer warning.
|
|
388
|
+
var migrationsRef = regexp.MustCompile(`(\w+)\.Migrations\b`)
|
|
389
|
+
|
|
390
|
+
// modulePath is the module line of go.mod: what "inside this tree" means.
|
|
391
|
+
func modulePath(root string) string {
|
|
392
|
+
source, err := os.ReadFile(filepath.Join(root, "go.mod"))
|
|
393
|
+
if err != nil {
|
|
394
|
+
return ""
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
for _, line := range strings.Split(string(source), "\n") {
|
|
398
|
+
if rest, ok := strings.CutPrefix(strings.TrimSpace(line), "module "); ok {
|
|
399
|
+
return strings.TrimSpace(rest)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return ""
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
func subdirs(root, rel string) []string {
|
|
407
|
+
entries, err := os.ReadDir(filepath.Join(root, filepath.FromSlash(rel)))
|
|
408
|
+
if err != nil {
|
|
409
|
+
return nil
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
var out []string
|
|
413
|
+
for _, entry := range entries {
|
|
414
|
+
if entry.IsDir() {
|
|
415
|
+
out = append(out, entry.Name())
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return out
|
|
420
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
|
|
10
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
func fragment(t *testing.T) (catalog.Catalog, []plugin.Warning) {
|
|
15
|
+
t.Helper()
|
|
16
|
+
|
|
17
|
+
resp := extract(
|
|
18
|
+
plugin.Input{Root: "../../examples/auth", Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"},
|
|
19
|
+
Options{Context: "auth", Service: "auth", Store: "pg", Name: "Auth database"},
|
|
20
|
+
)
|
|
21
|
+
if len(resp.Files) != 1 {
|
|
22
|
+
t.Fatalf("expected one fragment, got %d files", len(resp.Files))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var out catalog.Catalog
|
|
26
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
|
|
27
|
+
t.Fatal(err)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return out, resp.Warnings()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// One store, not one per aggregate: the migrations are numbered inside their
|
|
34
|
+
// own package but they are all applied to the same database.
|
|
35
|
+
func TestOneStoreHoldsEveryAggregatesTables(t *testing.T) {
|
|
36
|
+
cat, _ := fragment(t)
|
|
37
|
+
|
|
38
|
+
if len(cat.Stores) != 1 {
|
|
39
|
+
t.Fatalf("expected one store, got %d", len(cat.Stores))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
store := cat.Stores[0]
|
|
43
|
+
if store.ID != "auth.auth.pg" || store.Owner != "auth.auth" {
|
|
44
|
+
t.Errorf("store = %s owned by %s", store.ID, store.Owner)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
persists := map[string]string{}
|
|
48
|
+
for _, table := range store.Tables {
|
|
49
|
+
persists[table.Name] = table.Persists.Aggregate
|
|
50
|
+
}
|
|
51
|
+
if persists["users"] != "auth.auth.user" || persists["sessions"] != "auth.auth.session" || persists["lockouts"] != "auth.auth.lockout" {
|
|
52
|
+
t.Errorf("tables are linked to the wrong aggregates: %v", persists)
|
|
53
|
+
}
|
|
54
|
+
if store.Source != "../../examples/auth/internal/*/infrastructure/repository" {
|
|
55
|
+
t.Errorf("feature repository source = %q", store.Source)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The service half of the fragment says only which store it touches. What it
|
|
60
|
+
// is called and what it holds belong to the extractors that read those.
|
|
61
|
+
func TestFragmentClaimsOnlyTheLink(t *testing.T) {
|
|
62
|
+
cat, _ := fragment(t)
|
|
63
|
+
|
|
64
|
+
service := cat.Contexts[0].Services[0]
|
|
65
|
+
if len(service.Stores) != 1 || service.Stores[0] != "auth.auth.pg" {
|
|
66
|
+
t.Errorf("stores = %v", service.Stores)
|
|
67
|
+
}
|
|
68
|
+
if service.Name != "" || service.Readme != "" || len(service.Aggregates) != 0 {
|
|
69
|
+
t.Errorf("the sql fragment should not describe the service: %+v", service)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// A table brought by a dependency is real, and its DDL is not here. Silence
|
|
74
|
+
// would read as "there is no such table".
|
|
75
|
+
func TestSchemaAppliedFromOutsideTheTreeIsReported(t *testing.T) {
|
|
76
|
+
_, diagnostics := fragment(t)
|
|
77
|
+
|
|
78
|
+
var found string
|
|
79
|
+
for _, d := range diagnostics {
|
|
80
|
+
if strings.Contains(d.Message, "not in this tree") {
|
|
81
|
+
found = d.Message
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if !strings.Contains(found, "go-sdk/outbox") {
|
|
86
|
+
t.Errorf("the outbox schema should be reported by import path, got %q", found)
|
|
87
|
+
}
|
|
88
|
+
// The repository packages next door are imported under aliases that look
|
|
89
|
+
// just as foreign as the SDK's. Only the path tells them apart.
|
|
90
|
+
for _, d := range diagnostics {
|
|
91
|
+
if strings.Contains(d.Message, "repo") && strings.Contains(d.Message, "not in this tree") {
|
|
92
|
+
t.Errorf("a package from this module was called foreign: %q", d.Message)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// writeTree lays out a service under a temporary root: one file per path.
|
|
98
|
+
func writeTree(t *testing.T, files map[string]string) string {
|
|
99
|
+
t.Helper()
|
|
100
|
+
|
|
101
|
+
root := t.TempDir()
|
|
102
|
+
for rel, contents := range files {
|
|
103
|
+
full := filepath.Join(root, filepath.FromSlash(rel))
|
|
104
|
+
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
|
105
|
+
t.Fatal(err)
|
|
106
|
+
}
|
|
107
|
+
if err := os.WriteFile(full, []byte(contents), 0o644); err != nil {
|
|
108
|
+
t.Fatal(err)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return root
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// A projector package is the layout's word for a projection: every table it
|
|
116
|
+
// creates has that role. Whose rows it pictures is not in the layout, so it is
|
|
117
|
+
// taken from the migration when written there and left out when not - a
|
|
118
|
+
// projection of another service's aggregate has no directory here to be named
|
|
119
|
+
// by. The repository packages beside it are read exactly as before.
|
|
120
|
+
func TestProjectorPackagesAreProjections(t *testing.T) {
|
|
121
|
+
root := writeTree(t, map[string]string{
|
|
122
|
+
"internal/infrastructure/repository/route/migrations/0001_routes.sql": `
|
|
123
|
+
CREATE TABLE routes (
|
|
124
|
+
id text PRIMARY KEY,
|
|
125
|
+
version bigint NOT NULL
|
|
126
|
+
);
|
|
127
|
+
CREATE TABLE route_legs (
|
|
128
|
+
route_id text NOT NULL REFERENCES routes (id),
|
|
129
|
+
seq integer NOT NULL
|
|
130
|
+
);`,
|
|
131
|
+
"internal/infrastructure/projector/route_board/migrations/0001_route_board.sql": `
|
|
132
|
+
-- Stops with the address to print, rebuilt from OrderPlaced.
|
|
133
|
+
-- aggregate: shop.oms.order
|
|
134
|
+
CREATE TABLE route_board (
|
|
135
|
+
order_id text PRIMARY KEY,
|
|
136
|
+
-- from: shop.oms.pg.orders.ship_to
|
|
137
|
+
address text NOT NULL,
|
|
138
|
+
projected_at timestamptz NOT NULL
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
CREATE TABLE route_board_cursor (
|
|
142
|
+
topic text PRIMARY KEY,
|
|
143
|
+
-- from: routes.version
|
|
144
|
+
position bigint NOT NULL
|
|
145
|
+
);`,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
resp := extract(plugin.Input{Root: root}, Options{Context: "shop", Service: "delivery"})
|
|
149
|
+
if len(resp.Files) != 1 {
|
|
150
|
+
t.Fatalf("expected one fragment, got %d files", len(resp.Files))
|
|
151
|
+
}
|
|
152
|
+
var cat catalog.Catalog
|
|
153
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &cat); err != nil {
|
|
154
|
+
t.Fatal(err)
|
|
155
|
+
}
|
|
156
|
+
if len(cat.Stores) != 1 {
|
|
157
|
+
t.Fatalf("expected one store, got %d", len(cat.Stores))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
byName := map[string]catalog.Table{}
|
|
161
|
+
for _, table := range cat.Stores[0].Tables {
|
|
162
|
+
byName[table.Name] = table
|
|
163
|
+
}
|
|
164
|
+
if len(byName) != 4 {
|
|
165
|
+
t.Fatalf("expected four tables, got %v", byName)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// The repository side is unchanged.
|
|
169
|
+
if byName["routes"].Role != catalog.TableRoleAggregateRoot || byName["routes"].Persists.Aggregate != "shop.delivery.route" {
|
|
170
|
+
t.Errorf("routes = %+v", byName["routes"])
|
|
171
|
+
}
|
|
172
|
+
if byName["route_legs"].Role != catalog.TableRoleChild || byName["route_legs"].Persists.Aggregate != "shop.delivery.route" {
|
|
173
|
+
t.Errorf("route_legs = %+v", byName["route_legs"])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
board := byName["route_board"]
|
|
177
|
+
if board.ID != "shop.delivery.pg.route_board" || board.Role != catalog.TableRoleProjection {
|
|
178
|
+
t.Errorf("route_board = %+v", board)
|
|
179
|
+
}
|
|
180
|
+
if board.Persists == nil || board.Persists.Aggregate != "shop.oms.order" {
|
|
181
|
+
t.Errorf("the migration says whose picture this is: %+v", board.Persists)
|
|
182
|
+
}
|
|
183
|
+
for _, column := range board.Columns {
|
|
184
|
+
if column.Name != "address" {
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
if len(column.From) != 1 || column.From[0] != "shop.oms.pg.orders.ship_to" {
|
|
188
|
+
t.Errorf("address.from = %v", column.From)
|
|
189
|
+
}
|
|
190
|
+
if column.Maps != "" {
|
|
191
|
+
t.Errorf("a projection column maps to no aggregate field, got %q", column.Maps)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
cursor := byName["route_board_cursor"]
|
|
196
|
+
if cursor.Role != catalog.TableRoleProjection {
|
|
197
|
+
t.Errorf("every table a projector creates is a projection, got %q", cursor.Role)
|
|
198
|
+
}
|
|
199
|
+
if cursor.Persists != nil {
|
|
200
|
+
t.Errorf("no note, no link: %+v", cursor.Persists)
|
|
201
|
+
}
|
|
202
|
+
for _, column := range cursor.Columns {
|
|
203
|
+
if column.Name == "position" && (len(column.From) != 1 || column.From[0] != "shop.delivery.pg.routes.version") {
|
|
204
|
+
t.Errorf("a copy inside the store gets the store put back on, got %v", column.From)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for _, d := range resp.Warnings() {
|
|
209
|
+
if strings.Contains(d.Message, "no migrations") {
|
|
210
|
+
t.Errorf("the store has tables, yet: %q", d.Message)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// A service with projectors and no repositories still keeps state.
|
|
216
|
+
func TestAStoreOfOnlyProjectionsIsStillAStore(t *testing.T) {
|
|
217
|
+
root := writeTree(t, map[string]string{
|
|
218
|
+
"internal/infrastructure/projector/user_directory/migrations/0001_user_directory.sql": `
|
|
219
|
+
CREATE TABLE user_directory (
|
|
220
|
+
user_id text PRIMARY KEY,
|
|
221
|
+
email text NOT NULL
|
|
222
|
+
);`,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
resp := extract(plugin.Input{Root: root}, Options{Context: "shop", Service: "bff"})
|
|
226
|
+
var cat catalog.Catalog
|
|
227
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &cat); err != nil {
|
|
228
|
+
t.Fatal(err)
|
|
229
|
+
}
|
|
230
|
+
if len(cat.Stores) != 1 || len(cat.Stores[0].Tables) != 1 || cat.Stores[0].Tables[0].Role != catalog.TableRoleProjection {
|
|
231
|
+
t.Errorf("stores = %+v", cat.Stores)
|
|
232
|
+
}
|
|
233
|
+
}
|