@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,338 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"sort"
|
|
9
|
+
"strings"
|
|
10
|
+
|
|
11
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
12
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// LockName is what fetch-csr writes beside each vendored schema. Finding one
|
|
16
|
+
// is what makes a directory a subject rather than a directory that happens to
|
|
17
|
+
// hold a .avsc.
|
|
18
|
+
const LockName = "csr.lock.json"
|
|
19
|
+
|
|
20
|
+
func extract(in plugin.Input, opts Options) (plugin.Response, error) {
|
|
21
|
+
b := &plugin.Builder{}
|
|
22
|
+
root := in.Root
|
|
23
|
+
|
|
24
|
+
if opts.Context == "" {
|
|
25
|
+
opts.Context = filepath.Base(root)
|
|
26
|
+
}
|
|
27
|
+
if opts.Service == "" {
|
|
28
|
+
opts.Service = filepath.Base(root)
|
|
29
|
+
}
|
|
30
|
+
service := opts.Context + "." + opts.Service
|
|
31
|
+
strategy := strategyOf(opts.Strategy)
|
|
32
|
+
|
|
33
|
+
locks, err := find(root, opts.Paths)
|
|
34
|
+
if err != nil {
|
|
35
|
+
return plugin.Response{}, err
|
|
36
|
+
}
|
|
37
|
+
if len(locks) == 0 {
|
|
38
|
+
b.Warn(service, "no "+LockName+" under "+root+"; nothing was fetched from a schema registry, or it was fetched somewhere this step is not looking")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
defs := map[string]catalog.TypeDef{}
|
|
42
|
+
channels := newChannels()
|
|
43
|
+
|
|
44
|
+
for _, at := range locks {
|
|
45
|
+
if err := readSubject(b, at, root, service, strategy, opts, defs, channels); err != nil {
|
|
46
|
+
return plugin.Response{}, err
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// A field pointing at a shape nobody vendored would fail the catalog's own
|
|
51
|
+
// validation, and the reference is a real fact worth keeping as a label
|
|
52
|
+
// even when the shape behind it is not in this estate. So the label stays
|
|
53
|
+
// and the ref goes.
|
|
54
|
+
drop(defs, b, service)
|
|
55
|
+
|
|
56
|
+
fragment := catalog.Catalog{
|
|
57
|
+
GeneratedAt: in.GeneratedAt,
|
|
58
|
+
Commit: in.Commit,
|
|
59
|
+
Contexts: []catalog.BoundedContext{{
|
|
60
|
+
ID: opts.Context,
|
|
61
|
+
Slug: opts.Context,
|
|
62
|
+
// Named by whichever source knows the name, the way the AsyncAPI
|
|
63
|
+
// and OpenAPI fragments leave it: a registry describes what a
|
|
64
|
+
// service puts on a topic, not what the service is called.
|
|
65
|
+
Services: []catalog.Service{{
|
|
66
|
+
ID: service,
|
|
67
|
+
Slug: opts.Service,
|
|
68
|
+
Provides: []catalog.RpcService{},
|
|
69
|
+
Consumes: []catalog.RpcCall{},
|
|
70
|
+
Aggregates: []catalog.Aggregate{},
|
|
71
|
+
Channels: channels.all(),
|
|
72
|
+
}},
|
|
73
|
+
}},
|
|
74
|
+
Defs: defs,
|
|
75
|
+
Flows: []catalog.Flow{},
|
|
76
|
+
Adrs: []catalog.Adr{},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
80
|
+
if err != nil {
|
|
81
|
+
return plugin.Response{}, err
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
b.File(firstNonEmpty(opts.Out, "schemas.json"), string(encoded)+"\n")
|
|
85
|
+
|
|
86
|
+
return b.Response(), nil
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// readSubject reads one vendored subject: its lock, its schema, and the two
|
|
90
|
+
// facts they add up to.
|
|
91
|
+
func readSubject(
|
|
92
|
+
b *plugin.Builder,
|
|
93
|
+
at, root, service, strategy string,
|
|
94
|
+
opts Options,
|
|
95
|
+
defs map[string]catalog.TypeDef,
|
|
96
|
+
channels *channelSet,
|
|
97
|
+
) error {
|
|
98
|
+
dir := filepath.Dir(at)
|
|
99
|
+
where := filepath.ToSlash(at)
|
|
100
|
+
|
|
101
|
+
raw, err := os.ReadFile(at)
|
|
102
|
+
if err != nil {
|
|
103
|
+
return err
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
var lock Lock
|
|
107
|
+
if err := json.Unmarshal(raw, &lock); err != nil {
|
|
108
|
+
return fmt.Errorf("%s: %w", where, err)
|
|
109
|
+
}
|
|
110
|
+
if len(lock.Subjects) != 1 {
|
|
111
|
+
return fmt.Errorf("%s names %d subjects; expected exactly one", where, len(lock.Subjects))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
entry := lock.Subjects[0]
|
|
115
|
+
if len(entry.Files) == 0 {
|
|
116
|
+
b.Warn(entry.Subject, where+" names no schema file")
|
|
117
|
+
|
|
118
|
+
return nil
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
schemaAt := filepath.Join(dir, filepath.FromSlash(entry.Files[0].Path))
|
|
122
|
+
schema, err := os.ReadFile(schemaAt)
|
|
123
|
+
if err != nil {
|
|
124
|
+
return fmt.Errorf("%s: %w", where, err)
|
|
125
|
+
}
|
|
126
|
+
source := filepath.ToSlash(schemaAt)
|
|
127
|
+
|
|
128
|
+
declared, shapes, err := shapesOf(entry, schema, b, source)
|
|
129
|
+
if err != nil {
|
|
130
|
+
return fmt.Errorf("%s: %w", source, err)
|
|
131
|
+
}
|
|
132
|
+
for name, def := range shapes {
|
|
133
|
+
// Two subjects vendoring one shared record is the ordinary case - it
|
|
134
|
+
// is what a registry reference is for - and the two copies are the
|
|
135
|
+
// same bytes, so the first one wins and the second is not a conflict.
|
|
136
|
+
if _, held := defs[name]; !held {
|
|
137
|
+
defs[name] = def
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
registered := resolve(entry.Subject, declared, strategy)
|
|
142
|
+
if registered.Topic == "" {
|
|
143
|
+
// RecordNameStrategy says nothing about topics on purpose, so this is
|
|
144
|
+
// not a gap to report. The shape is still the fact worth having.
|
|
145
|
+
return nil
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if registered.Key {
|
|
149
|
+
// A key is part of every message on the topic rather than a message on
|
|
150
|
+
// it, so its shape is kept and its name is not put on the channel.
|
|
151
|
+
return nil
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
channels.add(registered.Topic, catalog.ChannelMessage{
|
|
155
|
+
Name: registered.Record,
|
|
156
|
+
Doc: fmt.Sprintf("Registered as %s version %d.", entry.Subject, entry.Version),
|
|
157
|
+
Direction: directionFor(entry.Subject, opts),
|
|
158
|
+
}, source)
|
|
159
|
+
|
|
160
|
+
return nil
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// shapesOf reads the schema according to its declared type.
|
|
164
|
+
func shapesOf(entry LockSubject, schema []byte, b *plugin.Builder, source string) (string, map[string]catalog.TypeDef, error) {
|
|
165
|
+
switch strings.ToUpper(entry.SchemaType) {
|
|
166
|
+
case "", "AVRO":
|
|
167
|
+
return avro(schema)
|
|
168
|
+
|
|
169
|
+
case "JSON":
|
|
170
|
+
return jsonSchema(schema, entry.Subject)
|
|
171
|
+
|
|
172
|
+
case "PROTOBUF":
|
|
173
|
+
// Reading protobuf properly is extract-proto's whole job, and a
|
|
174
|
+
// second, worse parser here would be a second answer to one question.
|
|
175
|
+
b.Warn(entry.Subject, source+" is protobuf; its topic is read but its shape is not. Point extract-proto at the vendored directory for the fields.")
|
|
176
|
+
|
|
177
|
+
return "", nil, nil
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return "", nil, fmt.Errorf("unknown schema type %q", entry.SchemaType)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// directionFor is which way a subject's channel travels. A registry records no
|
|
184
|
+
// producer and no consumer, so this is told rather than read.
|
|
185
|
+
func directionFor(subject string, opts Options) catalog.ChannelDirection {
|
|
186
|
+
declared, named := opts.Subjects[subject]
|
|
187
|
+
if !named {
|
|
188
|
+
declared = opts.Direction
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if declared == string(catalog.ChannelReceive) {
|
|
192
|
+
return catalog.ChannelReceive
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return catalog.ChannelSend
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// channelSet collects topics as the subjects are read. Several subjects on one
|
|
199
|
+
// topic - a key and a value, or two record types multiplexed onto it - are one
|
|
200
|
+
// channel carrying several messages.
|
|
201
|
+
type channelSet struct {
|
|
202
|
+
byTopic map[string]*catalog.Channel
|
|
203
|
+
seen map[string]bool
|
|
204
|
+
order []string
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
func newChannels() *channelSet {
|
|
208
|
+
return &channelSet{byTopic: map[string]*catalog.Channel{}, seen: map[string]bool{}}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
func (c *channelSet) add(topic string, message catalog.ChannelMessage, source string) {
|
|
212
|
+
held, known := c.byTopic[topic]
|
|
213
|
+
if !known {
|
|
214
|
+
held = &catalog.Channel{
|
|
215
|
+
Address: topic,
|
|
216
|
+
Messages: []catalog.ChannelMessage{},
|
|
217
|
+
Source: source,
|
|
218
|
+
}
|
|
219
|
+
c.byTopic[topic] = held
|
|
220
|
+
c.order = append(c.order, topic)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
key := topic + " " + string(message.Direction) + " " + message.Name
|
|
224
|
+
if c.seen[key] {
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
c.seen[key] = true
|
|
228
|
+
|
|
229
|
+
held.Messages = append(held.Messages, message)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// all is the channels in topic order, and their messages in name order. Sorted
|
|
233
|
+
// rather than kept in the order the tree happened to be walked in, because the
|
|
234
|
+
// fragment is committed and an unstable order would rewrite it every run.
|
|
235
|
+
func (c *channelSet) all() []catalog.Channel {
|
|
236
|
+
sort.Strings(c.order)
|
|
237
|
+
|
|
238
|
+
out := make([]catalog.Channel, 0, len(c.order))
|
|
239
|
+
for _, topic := range c.order {
|
|
240
|
+
held := c.byTopic[topic]
|
|
241
|
+
sort.Slice(held.Messages, func(i, j int) bool {
|
|
242
|
+
if held.Messages[i].Name != held.Messages[j].Name {
|
|
243
|
+
return held.Messages[i].Name < held.Messages[j].Name
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return held.Messages[i].Direction < held.Messages[j].Direction
|
|
247
|
+
})
|
|
248
|
+
out = append(out, *held)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return out
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// drop clears a field's ref when nothing in this estate declares the shape it
|
|
255
|
+
// names, which is the ordinary case for a record referenced across an estate
|
|
256
|
+
// boundary. The catalog validates that every ref resolves, so leaving one
|
|
257
|
+
// dangling would fail the whole run over a fact that is true.
|
|
258
|
+
func drop(defs map[string]catalog.TypeDef, b *plugin.Builder, service string) {
|
|
259
|
+
var dangling []string
|
|
260
|
+
|
|
261
|
+
for name, def := range defs {
|
|
262
|
+
for i, field := range def.Fields {
|
|
263
|
+
if field.Ref == "" {
|
|
264
|
+
continue
|
|
265
|
+
}
|
|
266
|
+
if _, known := defs[field.Ref]; known {
|
|
267
|
+
continue
|
|
268
|
+
}
|
|
269
|
+
dangling = append(dangling, field.Ref)
|
|
270
|
+
def.Fields[i].Ref = ""
|
|
271
|
+
}
|
|
272
|
+
defs[name] = def
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if len(dangling) == 0 {
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
sort.Strings(dangling)
|
|
280
|
+
b.Warn(service, "these shapes are referenced but not vendored, so they are named without a shape: "+
|
|
281
|
+
strings.Join(unique(dangling), ", "))
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// find locates every lock under the root, or under the declared paths.
|
|
285
|
+
//
|
|
286
|
+
// Sorted, so the fragment comes out the same way on every filesystem: WalkDir
|
|
287
|
+
// is ordered, but a manifest that names two paths is not.
|
|
288
|
+
func find(root string, paths []string) ([]string, error) {
|
|
289
|
+
roots := []string{root}
|
|
290
|
+
if len(paths) > 0 {
|
|
291
|
+
roots = roots[:0]
|
|
292
|
+
for _, at := range paths {
|
|
293
|
+
roots = append(roots, filepath.Join(root, filepath.FromSlash(at)))
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
var found []string
|
|
298
|
+
for _, at := range roots {
|
|
299
|
+
err := filepath.WalkDir(at, func(p string, entry os.DirEntry, err error) error {
|
|
300
|
+
if err != nil {
|
|
301
|
+
// A named path that is not there is worth reporting; one
|
|
302
|
+
// inside a walk is not this plugin's to explain.
|
|
303
|
+
if p == at && os.IsNotExist(err) {
|
|
304
|
+
return fmt.Errorf("%s does not exist", filepath.ToSlash(at))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return err
|
|
308
|
+
}
|
|
309
|
+
if !entry.IsDir() && entry.Name() == LockName {
|
|
310
|
+
found = append(found, p)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return nil
|
|
314
|
+
})
|
|
315
|
+
if err != nil {
|
|
316
|
+
return nil, err
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
sort.Strings(found)
|
|
321
|
+
|
|
322
|
+
return found, nil
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
func unique(values []string) []string {
|
|
326
|
+
out := values[:0]
|
|
327
|
+
seen := map[string]bool{}
|
|
328
|
+
|
|
329
|
+
for _, value := range values {
|
|
330
|
+
if seen[value] {
|
|
331
|
+
continue
|
|
332
|
+
}
|
|
333
|
+
seen[value] = true
|
|
334
|
+
out = append(out, value)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return out
|
|
338
|
+
}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"strings"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
9
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const estate = "testdata/estate"
|
|
13
|
+
|
|
14
|
+
func fragmentFrom(t *testing.T, opts Options) (catalog.Catalog, plugin.Response) {
|
|
15
|
+
t.Helper()
|
|
16
|
+
|
|
17
|
+
resp, err := extract(plugin.Input{Root: estate, Commit: "abc123", GeneratedAt: "2026-09-05T00:00:00Z"}, opts)
|
|
18
|
+
if err != nil {
|
|
19
|
+
t.Fatal(err)
|
|
20
|
+
}
|
|
21
|
+
if len(resp.Files) != 1 {
|
|
22
|
+
t.Fatalf("the extractor named %d files, want 1", len(resp.Files))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var fragment catalog.Catalog
|
|
26
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &fragment); err != nil {
|
|
27
|
+
t.Fatal(err)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return fragment, resp
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func defaults() Options {
|
|
34
|
+
return Options{Context: "shop", Service: "oms", Paths: []string{"vendor/schemas"}}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func channelsOf(t *testing.T, fragment catalog.Catalog) []catalog.Channel {
|
|
38
|
+
t.Helper()
|
|
39
|
+
|
|
40
|
+
if len(fragment.Contexts) != 1 || len(fragment.Contexts[0].Services) != 1 {
|
|
41
|
+
t.Fatalf("the fragment does not describe one service: %+v", fragment.Contexts)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return fragment.Contexts[0].Services[0].Channels
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func fieldsOf(t *testing.T, fragment catalog.Catalog, name string) []catalog.Field {
|
|
48
|
+
t.Helper()
|
|
49
|
+
|
|
50
|
+
def, known := fragment.Defs[name]
|
|
51
|
+
if !known {
|
|
52
|
+
var held []string
|
|
53
|
+
for key := range fragment.Defs {
|
|
54
|
+
held = append(held, key)
|
|
55
|
+
}
|
|
56
|
+
t.Fatalf("no def named %s; the fragment holds %v", name, held)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return def.Fields
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func fieldNamed(t *testing.T, fields []catalog.Field, name string) catalog.Field {
|
|
63
|
+
t.Helper()
|
|
64
|
+
|
|
65
|
+
for _, field := range fields {
|
|
66
|
+
if field.Name == name {
|
|
67
|
+
return field
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
t.Fatalf("no field named %s in %+v", name, fields)
|
|
71
|
+
|
|
72
|
+
return catalog.Field{}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func TestTopicsComeFromSubjects(t *testing.T) {
|
|
76
|
+
fragment, _ := fragmentFrom(t, defaults())
|
|
77
|
+
|
|
78
|
+
channels := channelsOf(t, fragment)
|
|
79
|
+
if len(channels) != 3 {
|
|
80
|
+
t.Fatalf("channels: %+v", channels)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Sorted by address, so the fragment is the same on every run.
|
|
84
|
+
var addresses []string
|
|
85
|
+
for _, channel := range channels {
|
|
86
|
+
addresses = append(addresses, channel.Address)
|
|
87
|
+
}
|
|
88
|
+
if got := strings.Join(addresses, ","); got != "shop.oms.order,shop.oms.route,shop.oms.shipment" {
|
|
89
|
+
t.Errorf("addresses %q", got)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
orders := channels[0]
|
|
93
|
+
// The key subject shares the topic with the value one and adds no message:
|
|
94
|
+
// a key is part of every message on the channel, not a message on it.
|
|
95
|
+
if len(orders.Messages) != 1 {
|
|
96
|
+
t.Fatalf("shop.oms.order carries %+v", orders.Messages)
|
|
97
|
+
}
|
|
98
|
+
if orders.Messages[0].Name != "shop.oms.OrderPlaced" {
|
|
99
|
+
t.Errorf("message name %q", orders.Messages[0].Name)
|
|
100
|
+
}
|
|
101
|
+
if orders.Messages[0].Direction != catalog.ChannelSend {
|
|
102
|
+
t.Errorf("direction %q, want send by default", orders.Messages[0].Direction)
|
|
103
|
+
}
|
|
104
|
+
if !strings.Contains(orders.Messages[0].Doc, "version 3") {
|
|
105
|
+
t.Errorf("the message does not say which registration it is: %q", orders.Messages[0].Doc)
|
|
106
|
+
}
|
|
107
|
+
if orders.Source != "testdata/estate/vendor/schemas/shop.oms.order-value/v3.avsc" {
|
|
108
|
+
t.Errorf("source %q", orders.Source)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func TestDirectionIsToldPerSubject(t *testing.T) {
|
|
113
|
+
opts := defaults()
|
|
114
|
+
opts.Direction = "send"
|
|
115
|
+
opts.Subjects = map[string]string{"shop.oms.shipment-value": "receive"}
|
|
116
|
+
|
|
117
|
+
fragment, _ := fragmentFrom(t, opts)
|
|
118
|
+
|
|
119
|
+
for _, channel := range channelsOf(t, fragment) {
|
|
120
|
+
want := catalog.ChannelSend
|
|
121
|
+
if channel.Address == "shop.oms.shipment" {
|
|
122
|
+
want = catalog.ChannelReceive
|
|
123
|
+
}
|
|
124
|
+
for _, message := range channel.Messages {
|
|
125
|
+
if message.Direction != want {
|
|
126
|
+
t.Errorf("%s carries %s %s, want %s", channel.Address, message.Name, message.Direction, want)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func TestAvroShapesLandInDefs(t *testing.T) {
|
|
133
|
+
fragment, _ := fragmentFrom(t, defaults())
|
|
134
|
+
|
|
135
|
+
fields := fieldsOf(t, fragment, "shop.oms.OrderPlaced")
|
|
136
|
+
|
|
137
|
+
for _, want := range []catalog.Field{
|
|
138
|
+
{Name: "order_id", Type: "string", Doc: "The order's own id."},
|
|
139
|
+
{Name: "note", Type: "string?", Doc: "What the customer wrote in the box."},
|
|
140
|
+
{Name: "labels", Type: "map<string, string>"},
|
|
141
|
+
{Name: "placed_at", Type: "timestamp-millis"},
|
|
142
|
+
{Name: "total", Type: "decimal(12,2)"},
|
|
143
|
+
} {
|
|
144
|
+
got := fieldNamed(t, fields, want.Name)
|
|
145
|
+
if got.Type != want.Type || got.Doc != want.Doc {
|
|
146
|
+
t.Errorf("%s is %+v, want type %q doc %q", want.Name, got, want.Type, want.Doc)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// A record vendored as its own subject is a shape this one points at, and
|
|
151
|
+
// the ref is what makes the two one shape rather than two.
|
|
152
|
+
buyer := fieldNamed(t, fields, "buyer")
|
|
153
|
+
if buyer.Type != "shop.oms.Party" || buyer.Ref != "shop.oms.Party" {
|
|
154
|
+
t.Errorf("buyer is %+v", buyer)
|
|
155
|
+
}
|
|
156
|
+
if got := fieldsOf(t, fragment, "shop.oms.Party"); len(got) != 2 {
|
|
157
|
+
t.Errorf("shop.oms.Party: %+v", got)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// A record inlined in a field gets its own def, under the namespace it
|
|
161
|
+
// inherits rather than none.
|
|
162
|
+
lines := fieldNamed(t, fields, "lines")
|
|
163
|
+
if lines.Type != "shop.oms.Line[]" || lines.Ref != "" {
|
|
164
|
+
t.Errorf("lines is %+v", lines)
|
|
165
|
+
}
|
|
166
|
+
if got := fieldsOf(t, fragment, "shop.oms.Line"); len(got) != 2 {
|
|
167
|
+
t.Errorf("shop.oms.Line: %+v", got)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Field order is the order the schema lists them in - it is the producer's
|
|
171
|
+
// statement about the record, and re-sorting it would lose that.
|
|
172
|
+
var order []string
|
|
173
|
+
for _, field := range fields {
|
|
174
|
+
order = append(order, field.Name)
|
|
175
|
+
}
|
|
176
|
+
if got := strings.Join(order, ","); got != "order_id,buyer,note,lines,labels,placed_at,total" {
|
|
177
|
+
t.Errorf("field order %q", got)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
func TestJSONSchemaShapesLandInDefs(t *testing.T) {
|
|
182
|
+
fragment, _ := fragmentFrom(t, defaults())
|
|
183
|
+
|
|
184
|
+
fields := fieldsOf(t, fragment, "shop.oms.ShipmentBooked")
|
|
185
|
+
|
|
186
|
+
// The order the document writes its properties in, which decoding into a
|
|
187
|
+
// map would have lost.
|
|
188
|
+
var order []string
|
|
189
|
+
for _, field := range fields {
|
|
190
|
+
order = append(order, field.Name)
|
|
191
|
+
}
|
|
192
|
+
if got := strings.Join(order, ","); got != "shipment_id,carrier,booked_at,weight_kg,stops,address" {
|
|
193
|
+
t.Errorf("property order %q", got)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for _, want := range []catalog.Field{
|
|
197
|
+
{Name: "shipment_id", Type: "string", Doc: "The booking's own id."},
|
|
198
|
+
{Name: "booked_at", Type: "date-time?"},
|
|
199
|
+
{Name: "weight_kg", Type: "number?"},
|
|
200
|
+
{Name: "stops", Type: "string[]?"},
|
|
201
|
+
} {
|
|
202
|
+
got := fieldNamed(t, fields, want.Name)
|
|
203
|
+
if got.Type != want.Type || got.Doc != want.Doc {
|
|
204
|
+
t.Errorf("%s is %+v, want type %q doc %q", want.Name, got, want.Type, want.Doc)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A $ref resolves to the definition the document keeps to one side.
|
|
209
|
+
// The label stays the name the document uses; the ref carries the key the
|
|
210
|
+
// catalog files it under, which is qualified because `definitions` is
|
|
211
|
+
// local to a document and `defs` is not.
|
|
212
|
+
carrier := fieldNamed(t, fields, "carrier")
|
|
213
|
+
if carrier.Type != "Carrier" || carrier.Ref != "shop.oms.ShipmentBooked.Carrier" {
|
|
214
|
+
t.Errorf("carrier is %+v", carrier)
|
|
215
|
+
}
|
|
216
|
+
if got := fieldsOf(t, fragment, "shop.oms.ShipmentBooked.Carrier"); len(got) != 2 {
|
|
217
|
+
t.Errorf("Carrier: %+v", got)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// An object inlined with no title is still a shape; it is named for where
|
|
221
|
+
// it sits.
|
|
222
|
+
address := fieldNamed(t, fields, "address")
|
|
223
|
+
if address.Ref != "shop.oms.ShipmentBooked.address" {
|
|
224
|
+
t.Errorf("address is %+v", address)
|
|
225
|
+
}
|
|
226
|
+
inner := fieldsOf(t, fragment, "shop.oms.ShipmentBooked.address")
|
|
227
|
+
if fieldNamed(t, inner, "line1").Type != "string" {
|
|
228
|
+
t.Errorf("line1 is %+v", fieldNamed(t, inner, "line1"))
|
|
229
|
+
}
|
|
230
|
+
if fieldNamed(t, inner, "postcode").Type != "string?" {
|
|
231
|
+
t.Errorf("postcode is not optional: %+v", fieldNamed(t, inner, "postcode"))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
func TestProtobufIsNamedButNotShaped(t *testing.T) {
|
|
236
|
+
fragment, resp := fragmentFrom(t, defaults())
|
|
237
|
+
|
|
238
|
+
// The topic is still a fact, and it is the one thing extract-proto cannot
|
|
239
|
+
// say: a .proto file does not know which topic it was registered against.
|
|
240
|
+
var found bool
|
|
241
|
+
for _, channel := range channelsOf(t, fragment) {
|
|
242
|
+
if channel.Address == "shop.oms.route" {
|
|
243
|
+
found = true
|
|
244
|
+
if len(channel.Messages) != 1 || channel.Messages[0].Name != "shop.oms.route-value" {
|
|
245
|
+
t.Errorf("shop.oms.route carries %+v", channel.Messages)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if !found {
|
|
250
|
+
t.Error("the protobuf subject named no topic")
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
var said bool
|
|
254
|
+
for _, warning := range resp.Warnings() {
|
|
255
|
+
if strings.Contains(warning.Message, "extract-proto") {
|
|
256
|
+
said = true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if !said {
|
|
260
|
+
t.Errorf("nothing pointed at extract-proto for the shape: %+v", resp.Warnings())
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
func TestRecordStrategyNamesNoTopic(t *testing.T) {
|
|
265
|
+
opts := defaults()
|
|
266
|
+
opts.Strategy = StrategyRecord
|
|
267
|
+
|
|
268
|
+
fragment, _ := fragmentFrom(t, opts)
|
|
269
|
+
|
|
270
|
+
// RecordNameStrategy says nothing about topics on purpose - a record under
|
|
271
|
+
// it is reusable across many - so a fragment with no channels is the right
|
|
272
|
+
// answer rather than a gap.
|
|
273
|
+
if channels := channelsOf(t, fragment); len(channels) != 0 {
|
|
274
|
+
t.Errorf("channels: %+v", channels)
|
|
275
|
+
}
|
|
276
|
+
// The shapes are still there, which is the half the strategy does state.
|
|
277
|
+
if got := fieldsOf(t, fragment, "shop.oms.OrderPlaced"); len(got) != 7 {
|
|
278
|
+
t.Errorf("shop.oms.OrderPlaced: %+v", got)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
func TestTopicRecordStrategySplitsOnTheSchemasOwnName(t *testing.T) {
|
|
283
|
+
// "shop.oms.order-value" under this strategy is topic "shop.oms.order"
|
|
284
|
+
// and record "value" if you split at the hyphen, and neither half is
|
|
285
|
+
// reliable - so the schema's own full name is what the suffix is matched
|
|
286
|
+
// against.
|
|
287
|
+
for _, c := range []struct {
|
|
288
|
+
subject, declared, topic, record string
|
|
289
|
+
}{
|
|
290
|
+
{"orders-com.acme.Order", "com.acme.Order", "orders", "com.acme.Order"},
|
|
291
|
+
{"shop.oms.order-shop.oms.OrderPlaced", "shop.oms.OrderPlaced", "shop.oms.order", "shop.oms.OrderPlaced"},
|
|
292
|
+
{"orders-Order", "", "orders", "Order"},
|
|
293
|
+
} {
|
|
294
|
+
got := resolve(c.subject, c.declared, StrategyTopicRecord)
|
|
295
|
+
if got.Topic != c.topic || got.Record != c.record {
|
|
296
|
+
t.Errorf("%q -> %+v, want topic %q record %q", c.subject, got, c.topic, c.record)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
func TestTopicStrategyReadsTheSuffix(t *testing.T) {
|
|
302
|
+
for _, c := range []struct {
|
|
303
|
+
subject, declared, topic, record string
|
|
304
|
+
key bool
|
|
305
|
+
}{
|
|
306
|
+
{"shop.oms.order-value", "shop.oms.OrderPlaced", "shop.oms.order", "shop.oms.OrderPlaced", false},
|
|
307
|
+
{"shop.oms.order-key", "shop.oms.OrderKey", "shop.oms.order", "shop.oms.OrderKey", true},
|
|
308
|
+
// Registered some other way: it names a shape but cannot name a topic.
|
|
309
|
+
{"loose", "shop.oms.Loose", "", "shop.oms.Loose", false},
|
|
310
|
+
} {
|
|
311
|
+
got := resolve(c.subject, c.declared, StrategyTopic)
|
|
312
|
+
if got.Topic != c.topic || got.Record != c.record || got.Key != c.key {
|
|
313
|
+
t.Errorf("%q -> %+v", c.subject, got)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
func TestAMissingPathIsReported(t *testing.T) {
|
|
319
|
+
opts := defaults()
|
|
320
|
+
opts.Paths = []string{"vendor/nothing"}
|
|
321
|
+
|
|
322
|
+
_, err := extract(plugin.Input{Root: estate}, opts)
|
|
323
|
+
if err == nil || !strings.Contains(err.Error(), "does not exist") {
|
|
324
|
+
t.Errorf("a path that is not there was walked silently: %v", err)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
func TestAReferenceNobodyVendoredKeepsItsNameAndLosesItsRef(t *testing.T) {
|
|
329
|
+
// The catalog validates that every field ref resolves, so a ref pointing
|
|
330
|
+
// at a shape this estate does not hold would fail the whole run over a
|
|
331
|
+
// fact that is true: the schema really does reference it, and the shape
|
|
332
|
+
// really does live somewhere else.
|
|
333
|
+
resp, err := extract(
|
|
334
|
+
plugin.Input{Root: "testdata/dangling"},
|
|
335
|
+
Options{Context: "shop", Service: "oms", Paths: []string{"vendor/schemas"}},
|
|
336
|
+
)
|
|
337
|
+
if err != nil {
|
|
338
|
+
t.Fatal(err)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
var fragment catalog.Catalog
|
|
342
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &fragment); err != nil {
|
|
343
|
+
t.Fatal(err)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
actor := fieldNamed(t, fieldsOf(t, fragment, "shop.oms.AuditWritten"), "actor")
|
|
347
|
+
if actor.Type != "shop.identity.Actor" {
|
|
348
|
+
t.Errorf("the reference lost its name: %+v", actor)
|
|
349
|
+
}
|
|
350
|
+
if actor.Ref != "" {
|
|
351
|
+
t.Errorf("a ref was left pointing at a shape nothing declares: %+v", actor)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
for _, def := range fragment.Defs {
|
|
355
|
+
for _, field := range def.Fields {
|
|
356
|
+
if field.Ref == "" {
|
|
357
|
+
continue
|
|
358
|
+
}
|
|
359
|
+
if _, known := fragment.Defs[field.Ref]; !known {
|
|
360
|
+
t.Errorf("%s refs %s, which the fragment does not declare", field.Name, field.Ref)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
var said bool
|
|
366
|
+
for _, warning := range resp.Warnings() {
|
|
367
|
+
if strings.Contains(warning.Message, "shop.identity.Actor") {
|
|
368
|
+
said = true
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if !said {
|
|
372
|
+
t.Errorf("the unvendored reference was dropped silently: %+v", resp.Warnings())
|
|
373
|
+
}
|
|
374
|
+
}
|