@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,222 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// Turning a name as written into the thing it names.
|
|
4
|
+
//
|
|
5
|
+
// A proto reference is relative to where it was written: `Money` inside
|
|
6
|
+
// `shop.v1.Order` might mean `shop.v1.Order.Money`, `shop.v1.Money`, or
|
|
7
|
+
// `Money` at the root, and proto searches outwards in that order. A leading dot
|
|
8
|
+
// means the search is skipped and the name is already absolute.
|
|
9
|
+
//
|
|
10
|
+
// A name that resolves to nothing is NOT an error here. A narrowed vendored
|
|
11
|
+
// copy importing a file nobody vendored beside it is the normal case - see the
|
|
12
|
+
// header of parse.go - and the answer is to say the type is unresolved and
|
|
13
|
+
// carry on, not to refuse to describe the file.
|
|
14
|
+
|
|
15
|
+
import (
|
|
16
|
+
"sort"
|
|
17
|
+
"strings"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// scalars are proto's built-in types, which never resolve to a declaration.
|
|
21
|
+
var scalars = map[string]bool{
|
|
22
|
+
"double": true, "float": true, "int32": true, "int64": true,
|
|
23
|
+
"uint32": true, "uint64": true, "sint32": true, "sint64": true,
|
|
24
|
+
"fixed32": true, "fixed64": true, "sfixed32": true, "sfixed64": true,
|
|
25
|
+
"bool": true, "string": true, "bytes": true,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func isScalar(name string) bool { return scalars[name] }
|
|
29
|
+
|
|
30
|
+
// Index is every declaration in a set of files, by fully-qualified name.
|
|
31
|
+
type Index struct {
|
|
32
|
+
files []*File
|
|
33
|
+
|
|
34
|
+
messages map[string]*Message
|
|
35
|
+
enums map[string]*Enum
|
|
36
|
+
fileOf map[string]*File
|
|
37
|
+
fqnOf map[*Message]string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// NewIndex walks the files once and records every message and enum in them,
|
|
41
|
+
// nested ones included, under the name the rest of the world would use.
|
|
42
|
+
func NewIndex(files []*File) *Index {
|
|
43
|
+
ix := &Index{
|
|
44
|
+
files: files,
|
|
45
|
+
messages: map[string]*Message{},
|
|
46
|
+
enums: map[string]*Enum{},
|
|
47
|
+
fileOf: map[string]*File{},
|
|
48
|
+
fqnOf: map[*Message]string{},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for _, file := range files {
|
|
52
|
+
for _, m := range file.Messages {
|
|
53
|
+
ix.addMessage(file, file.Package, m)
|
|
54
|
+
}
|
|
55
|
+
for _, e := range file.Enums {
|
|
56
|
+
ix.addEnum(file, join(file.Package, e.Name), e)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return ix
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func (ix *Index) addMessage(file *File, scope string, m *Message) {
|
|
64
|
+
fqn := join(scope, m.Name)
|
|
65
|
+
ix.messages[fqn] = m
|
|
66
|
+
ix.fileOf[fqn] = file
|
|
67
|
+
ix.fqnOf[m] = fqn
|
|
68
|
+
|
|
69
|
+
for _, nested := range m.Messages {
|
|
70
|
+
ix.addMessage(file, fqn, nested)
|
|
71
|
+
}
|
|
72
|
+
for _, e := range m.Enums {
|
|
73
|
+
ix.addEnum(file, join(fqn, e.Name), e)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func (ix *Index) addEnum(file *File, fqn string, e *Enum) {
|
|
78
|
+
ix.enums[fqn] = e
|
|
79
|
+
ix.fileOf[fqn] = file
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Resolve finds what `name`, written inside `scope`, refers to.
|
|
83
|
+
//
|
|
84
|
+
// scope is a fully-qualified name - a package, or a message inside one. The
|
|
85
|
+
// search walks outwards from it, which is what proto itself does.
|
|
86
|
+
func (ix *Index) Resolve(scope, name string) (string, bool) {
|
|
87
|
+
if name == "" || isScalar(name) {
|
|
88
|
+
return "", false
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if strings.HasPrefix(name, ".") {
|
|
92
|
+
fqn := strings.TrimPrefix(name, ".")
|
|
93
|
+
|
|
94
|
+
return fqn, ix.declared(fqn)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
parts := strings.Split(scope, ".")
|
|
98
|
+
for i := len(parts); i >= 0; i-- {
|
|
99
|
+
fqn := join(strings.Join(parts[:i], "."), name)
|
|
100
|
+
if ix.declared(fqn) {
|
|
101
|
+
return fqn, true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return "", false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
func (ix *Index) declared(fqn string) bool {
|
|
109
|
+
_, message := ix.messages[fqn]
|
|
110
|
+
_, enum := ix.enums[fqn]
|
|
111
|
+
|
|
112
|
+
return message || enum
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func (ix *Index) Message(fqn string) *Message { return ix.messages[fqn] }
|
|
116
|
+
func (ix *Index) Enum(fqn string) *Enum { return ix.enums[fqn] }
|
|
117
|
+
func (ix *Index) FileOf(fqn string) *File { return ix.fileOf[fqn] }
|
|
118
|
+
|
|
119
|
+
// FQN is the name a message is known by, once it has been indexed.
|
|
120
|
+
func (ix *Index) FQN(m *Message) string { return ix.fqnOf[m] }
|
|
121
|
+
|
|
122
|
+
// Names is every declared name, sorted. Determinism is not a nicety here: the
|
|
123
|
+
// fragment these end up in is committed and compared by `gen:check`, so a map
|
|
124
|
+
// iterated in Go's order would produce a different file every run.
|
|
125
|
+
func (ix *Index) Names() []string {
|
|
126
|
+
out := make([]string, 0, len(ix.messages)+len(ix.enums))
|
|
127
|
+
for fqn := range ix.messages {
|
|
128
|
+
out = append(out, fqn)
|
|
129
|
+
}
|
|
130
|
+
for fqn := range ix.enums {
|
|
131
|
+
out = append(out, fqn)
|
|
132
|
+
}
|
|
133
|
+
sort.Strings(out)
|
|
134
|
+
|
|
135
|
+
return out
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// TypeRef is one field's type, resolved as far as it could be.
|
|
139
|
+
type TypeRef struct {
|
|
140
|
+
// Written is the type as the author typed it, rendered the way the catalog
|
|
141
|
+
// renders types: `[]LineItem`, `map[string]Money`, `Money`.
|
|
142
|
+
Written string
|
|
143
|
+
|
|
144
|
+
// FQN is what the named type resolves to, empty for a scalar or for a name
|
|
145
|
+
// nothing in the tree declares.
|
|
146
|
+
FQN string
|
|
147
|
+
|
|
148
|
+
// Unresolved is the name that could not be found, empty when there was
|
|
149
|
+
// nothing to find or the search succeeded. It is a diagnostic, not a
|
|
150
|
+
// failure: a narrowed vendored copy is expected to have some.
|
|
151
|
+
Unresolved string
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// resolveType renders a field's type and resolves the name inside it.
|
|
155
|
+
//
|
|
156
|
+
// A map is rendered `map[K]V` and resolved on its value: the key of a proto map
|
|
157
|
+
// is always a scalar, and the value is the half that can name a message.
|
|
158
|
+
func (ix *Index) resolveType(scope string, f *Field) TypeRef {
|
|
159
|
+
if key, value, ok := mapParts(f.Type); ok {
|
|
160
|
+
inner := ix.resolveType(scope, &Field{Type: value})
|
|
161
|
+
|
|
162
|
+
return TypeRef{
|
|
163
|
+
Written: "map[" + key + "]" + inner.Written,
|
|
164
|
+
FQN: inner.FQN,
|
|
165
|
+
Unresolved: inner.Unresolved,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ref := TypeRef{Written: shortName(f.Type)}
|
|
170
|
+
if f.Label == "repeated" {
|
|
171
|
+
ref.Written = "[]" + ref.Written
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if isScalar(f.Type) {
|
|
175
|
+
return ref
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if fqn, ok := ix.Resolve(scope, f.Type); ok {
|
|
179
|
+
ref.FQN = fqn
|
|
180
|
+
} else {
|
|
181
|
+
ref.Unresolved = f.Type
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return ref
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// mapParts splits `map<string, Money>` into its two arguments.
|
|
188
|
+
func mapParts(typ string) (string, string, bool) {
|
|
189
|
+
if !strings.HasPrefix(typ, "map<") || !strings.HasSuffix(typ, ">") {
|
|
190
|
+
return "", "", false
|
|
191
|
+
}
|
|
192
|
+
inner := typ[len("map<") : len(typ)-1]
|
|
193
|
+
comma := strings.Index(inner, ",")
|
|
194
|
+
if comma < 0 {
|
|
195
|
+
return "", "", false
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return strings.TrimSpace(inner[:comma]), strings.TrimSpace(inner[comma+1:]), true
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// shortName is the last segment of a dotted name.
|
|
202
|
+
//
|
|
203
|
+
// The catalog spells a type the way a reader says it - `Money`, not
|
|
204
|
+
// `shop.v1.Money` - and its `defs` keys are deliberately bare for the same
|
|
205
|
+
// reason: two sources meaning different things by `Money` should collide
|
|
206
|
+
// visibly rather than coexist as two quietly different entries.
|
|
207
|
+
func shortName(name string) string {
|
|
208
|
+
name = strings.TrimPrefix(name, ".")
|
|
209
|
+
if i := strings.LastIndex(name, "."); i >= 0 {
|
|
210
|
+
return name[i+1:]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return name
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
func join(scope, name string) string {
|
|
217
|
+
if scope == "" {
|
|
218
|
+
return name
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return scope + "." + name
|
|
222
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// Name resolution, on its own. A proto reference means whatever the innermost
|
|
4
|
+
// enclosing scope that declares it means, and a name nothing declares is a
|
|
5
|
+
// diagnostic rather than a failure.
|
|
6
|
+
|
|
7
|
+
import "testing"
|
|
8
|
+
|
|
9
|
+
func indexed(t *testing.T) *Index {
|
|
10
|
+
t.Helper()
|
|
11
|
+
|
|
12
|
+
orders, _ := parseFile(t, "orders.proto")
|
|
13
|
+
money, _ := parseFile(t, "money.proto")
|
|
14
|
+
|
|
15
|
+
return NewIndex([]*File{orders, money})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func TestResolvesAcrossFiles(t *testing.T) {
|
|
19
|
+
ix := indexed(t)
|
|
20
|
+
|
|
21
|
+
fqn, ok := ix.Resolve("shop.v1", "Money")
|
|
22
|
+
if !ok || fqn != "shop.v1.Money" {
|
|
23
|
+
t.Errorf("Money from shop.v1: %q %v", fqn, ok)
|
|
24
|
+
}
|
|
25
|
+
if ix.Message(fqn) == nil {
|
|
26
|
+
t.Error("the resolved name does not lead back to the message")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Proto searches outwards from where the name was written, so the nested type
|
|
31
|
+
// wins over anything of the same name further out.
|
|
32
|
+
func TestResolvesInnermostFirst(t *testing.T) {
|
|
33
|
+
ix := indexed(t)
|
|
34
|
+
|
|
35
|
+
fqn, ok := ix.Resolve("shop.v1.Order", "Address")
|
|
36
|
+
if !ok || fqn != "shop.v1.Order.Address" {
|
|
37
|
+
t.Errorf("Address from inside Order: %q %v", fqn, ok)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The same name written one scope out finds nothing, which is the point.
|
|
41
|
+
if _, ok := ix.Resolve("shop.v1", "Address"); ok {
|
|
42
|
+
t.Error("a nested type resolved from outside the message that nests it")
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func TestLeadingDotIsAbsolute(t *testing.T) {
|
|
47
|
+
ix := indexed(t)
|
|
48
|
+
|
|
49
|
+
fqn, ok := ix.Resolve("shop.v1.Order", ".shop.v1.Money")
|
|
50
|
+
if !ok || fqn != "shop.v1.Money" {
|
|
51
|
+
t.Errorf("absolute name: %q %v", fqn, ok)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func TestNestedEnumIsIndexed(t *testing.T) {
|
|
56
|
+
ix := indexed(t)
|
|
57
|
+
|
|
58
|
+
fqn, ok := ix.Resolve("shop.v1.Order", "Channel")
|
|
59
|
+
if !ok || fqn != "shop.v1.Order.Channel" {
|
|
60
|
+
t.Errorf("nested enum: %q %v", fqn, ok)
|
|
61
|
+
}
|
|
62
|
+
if ix.Enum(fqn) == nil {
|
|
63
|
+
t.Error("the resolved name does not lead back to the enum")
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The whole reason this parser is tolerant: a narrowed vendored copy imports
|
|
68
|
+
// files nobody vendored beside it, and it must still be describable.
|
|
69
|
+
func TestUnresolvedIsReportedNotFatal(t *testing.T) {
|
|
70
|
+
ix := indexed(t)
|
|
71
|
+
|
|
72
|
+
ref := ix.resolveType("shop.v1", &Field{Type: "Nowhere"})
|
|
73
|
+
if ref.Unresolved != "Nowhere" {
|
|
74
|
+
t.Errorf("an unknown type was not reported: %+v", ref)
|
|
75
|
+
}
|
|
76
|
+
if ref.Written != "Nowhere" {
|
|
77
|
+
t.Errorf("an unknown type must still be shown as written: %q", ref.Written)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Types are rendered the way the catalog renders types, and resolved on the
|
|
82
|
+
// half that can name a message.
|
|
83
|
+
func TestTypeRendering(t *testing.T) {
|
|
84
|
+
ix := indexed(t)
|
|
85
|
+
|
|
86
|
+
cases := []struct {
|
|
87
|
+
field Field
|
|
88
|
+
written, fqn string
|
|
89
|
+
}{
|
|
90
|
+
{Field{Type: "string"}, "string", ""},
|
|
91
|
+
{Field{Type: "Money"}, "Money", "shop.v1.Money"},
|
|
92
|
+
{Field{Type: "shop.v1.Money"}, "Money", "shop.v1.Money"},
|
|
93
|
+
{Field{Type: "LineItem", Label: "repeated"}, "[]LineItem", "shop.v1.LineItem"},
|
|
94
|
+
{Field{Type: "map<string, string>"}, "map[string]string", ""},
|
|
95
|
+
{Field{Type: "map<string, Money>"}, "map[string]Money", "shop.v1.Money"},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for _, c := range cases {
|
|
99
|
+
got := ix.resolveType("shop.v1", &c.field)
|
|
100
|
+
if got.Written != c.written || got.FQN != c.fqn {
|
|
101
|
+
t.Errorf("%s: %q -> %q (want %q -> %q)", c.field.Type, got.Written, got.FQN, c.written, c.fqn)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The fragment is committed and compared by gen:check, so anything derived from
|
|
107
|
+
// a map has to come out in the same order every run.
|
|
108
|
+
func TestNamesAreSorted(t *testing.T) {
|
|
109
|
+
names := indexed(t).Names()
|
|
110
|
+
|
|
111
|
+
for i := 1; i < len(names); i++ {
|
|
112
|
+
if names[i-1] > names[i] {
|
|
113
|
+
t.Fatalf("names are not sorted: %q before %q", names[i-1], names[i])
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if len(names) == 0 {
|
|
117
|
+
t.Fatal("nothing was indexed")
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Python Kafka extractor
|
|
2
|
+
|
|
3
|
+
`extract-python-kafka` reads Kafka client construction and calls from Python
|
|
4
|
+
syntax. It does not import or execute the project and it does not require a
|
|
5
|
+
Django, Flask, or other framework layout.
|
|
6
|
+
|
|
7
|
+
Supported clients:
|
|
8
|
+
|
|
9
|
+
- `confluent_kafka.Producer.produce` and `Consumer.subscribe`;
|
|
10
|
+
- `kafka.KafkaProducer.send` and constructor-based `KafkaConsumer` topics;
|
|
11
|
+
- `aiokafka.AIOKafkaProducer.send` / `send_and_wait` and constructor-based
|
|
12
|
+
`AIOKafkaConsumer` topics.
|
|
13
|
+
|
|
14
|
+
Literal topics, module constants, imported constants, environment defaults and
|
|
15
|
+
optional `settings.NAME` references are followed. Local producer factories are
|
|
16
|
+
followed to a supported constructor, so a singleton wrapper does not hide the
|
|
17
|
+
client. Topics that remain dynamic are reported and no channel is guessed.
|
|
18
|
+
|
|
19
|
+
The output uses generic `message` channels rather than domain events. It keeps
|
|
20
|
+
source-backed broker, security mode, idempotence, retry, compression, client
|
|
21
|
+
id, consumer group, key, headers and serializer facts. Authentication values
|
|
22
|
+
are never emitted. Partitions, replication and retention are broker-side facts
|
|
23
|
+
and remain explicitly unknown unless another catalog source declares them.
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"plugin": "python-kafka",
|
|
28
|
+
"in": "checkout",
|
|
29
|
+
"out": ".portolan/fragments/checkout-kafka",
|
|
30
|
+
"options": {
|
|
31
|
+
"context": "shop",
|
|
32
|
+
"service": "checkout",
|
|
33
|
+
"settings": "config.settings"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Python Kafka client calls in, generic message channels and flows out."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from typing import Any, Dict, List
|
|
9
|
+
|
|
10
|
+
import catalog
|
|
11
|
+
import kafka
|
|
12
|
+
from names import slug, title
|
|
13
|
+
from options import Options
|
|
14
|
+
from protocol import Builder, File, Input
|
|
15
|
+
from source import Project
|
|
16
|
+
|
|
17
|
+
MESSAGE = "message"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def extract(input_: Input, opts: Options, b: Builder, cwd: str = "") -> None:
|
|
21
|
+
cwd = cwd or os.getcwd()
|
|
22
|
+
root = os.path.abspath(os.path.join(cwd, input_.root))
|
|
23
|
+
|
|
24
|
+
def rel(path: str) -> str:
|
|
25
|
+
return os.path.relpath(path, cwd).replace(os.sep, "/")
|
|
26
|
+
|
|
27
|
+
source = os.path.normpath(os.path.join(root, opts.source or "."))
|
|
28
|
+
context = opts.context or os.path.basename(root)
|
|
29
|
+
service = opts.service or os.path.basename(root)
|
|
30
|
+
svc_id = context + "." + service
|
|
31
|
+
project = Project(root, source, rel)
|
|
32
|
+
for path, message in project.broken:
|
|
33
|
+
b.warn(path, "cannot be parsed, so nothing in it is read: %s" % message)
|
|
34
|
+
|
|
35
|
+
found = kafka.scan(project, opts.settings)
|
|
36
|
+
if not found.publishes and not found.subscriptions:
|
|
37
|
+
b.warn(svc_id, "no supported Python Kafka producer or consumer calls under %s" % rel(source))
|
|
38
|
+
for where, expr in sorted(set(found.dynamic_topics)):
|
|
39
|
+
b.warn(where, "Kafka topic is dynamic%s, so no channel is invented" % ((": `%s`" % expr) if expr else ""))
|
|
40
|
+
clients = [item.client for item in found.publishes] + [item.client for item in found.subscriptions]
|
|
41
|
+
for source_line in sorted({client.source for client in clients if "brokers" not in client.config}):
|
|
42
|
+
b.warn(source_line or svc_id, "Kafka broker endpoints are resolved at runtime, so none are claimed in the catalog")
|
|
43
|
+
|
|
44
|
+
sends = defaultdict(list)
|
|
45
|
+
receives = defaultdict(list)
|
|
46
|
+
for item in found.publishes:
|
|
47
|
+
sends[item.topic].append(item)
|
|
48
|
+
for item in found.subscriptions:
|
|
49
|
+
receives[item.topic].append(item)
|
|
50
|
+
for topic in sorted(set(sends) - set(receives)):
|
|
51
|
+
b.warn(svc_id, "publishes Kafka topic `%s`, but no consumer for it is proven in this source tree" % topic)
|
|
52
|
+
|
|
53
|
+
channels = [channel(topic, sends[topic], receives[topic]) for topic in sorted(set(sends) | set(receives))]
|
|
54
|
+
flows = []
|
|
55
|
+
for topic in sorted(sends):
|
|
56
|
+
for index, item in enumerate(sends[topic], 1):
|
|
57
|
+
flows.append(publish_flow(context, service, svc_id, item, index))
|
|
58
|
+
for topic in sorted(receives):
|
|
59
|
+
received_message = sends[topic][0].message if len({item.message for item in sends[topic]}) == 1 else "message"
|
|
60
|
+
for index, item in enumerate(receives[topic], 1):
|
|
61
|
+
flows.append(receive_flow(context, service, svc_id, item, received_message, index))
|
|
62
|
+
flows.sort(key=lambda flow: flow["slug"])
|
|
63
|
+
|
|
64
|
+
fragment: Dict[str, Any] = {
|
|
65
|
+
"generatedAt": input_.generated_at,
|
|
66
|
+
"commit": input_.commit,
|
|
67
|
+
"contexts": [
|
|
68
|
+
{
|
|
69
|
+
"id": context,
|
|
70
|
+
"slug": context,
|
|
71
|
+
"name": "",
|
|
72
|
+
"summary": "",
|
|
73
|
+
"services": [
|
|
74
|
+
{
|
|
75
|
+
"id": svc_id,
|
|
76
|
+
"slug": service,
|
|
77
|
+
"name": "",
|
|
78
|
+
"repo": "",
|
|
79
|
+
"path": "",
|
|
80
|
+
"readme": "",
|
|
81
|
+
"provides": [],
|
|
82
|
+
"consumes": [],
|
|
83
|
+
"aggregates": [],
|
|
84
|
+
"channels": channels,
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"defs": {},
|
|
90
|
+
"flows": flows,
|
|
91
|
+
"adrs": [],
|
|
92
|
+
}
|
|
93
|
+
b.files.append(File(name=opts.out or "kafka.json", contents=json.dumps(fragment, indent=2, ensure_ascii=False) + "\n"))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def channel(topic: str, sends: List[kafka.Publish], receives: List[kafka.Subscription]) -> Dict[str, Any]:
|
|
97
|
+
messages = []
|
|
98
|
+
seen = set()
|
|
99
|
+
for item in sends:
|
|
100
|
+
key = (item.message, "send")
|
|
101
|
+
if key not in seen:
|
|
102
|
+
messages.append(catalog.message(item.message, title(item.message), message_doc(item), "send"))
|
|
103
|
+
seen.add(key)
|
|
104
|
+
for item in receives:
|
|
105
|
+
# Kafka subscriptions dispatch records, and source often does not prove
|
|
106
|
+
# their schema. Match a single proven producer name; otherwise stay
|
|
107
|
+
# honest and call the record a message.
|
|
108
|
+
name = sends[0].message if len({sent.message for sent in sends}) == 1 else "message"
|
|
109
|
+
key = (name, "receive")
|
|
110
|
+
if key not in seen:
|
|
111
|
+
messages.append(catalog.message(name, title(name), consumer_doc(item), "receive"))
|
|
112
|
+
seen.add(key)
|
|
113
|
+
clients = []
|
|
114
|
+
for item in list(sends) + list(receives):
|
|
115
|
+
note = kafka.config_note(item.client)
|
|
116
|
+
if note not in clients:
|
|
117
|
+
clients.append(note)
|
|
118
|
+
doc = "Kafka message stream. " + ". ".join(clients) + ". Broker-side partitions, replication and retention are not declared by client code."
|
|
119
|
+
source = (sends[0].line if sends else receives[0].line) if sends or receives else ""
|
|
120
|
+
return catalog.channel(topic, MESSAGE, "Kafka · " + topic, doc, messages, source)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def message_doc(item: kafka.Publish) -> str:
|
|
124
|
+
bits = ["Published with %s" % item.client.library]
|
|
125
|
+
if item.message_expression:
|
|
126
|
+
bits.append("payload `%s`" % item.message_expression)
|
|
127
|
+
if item.key:
|
|
128
|
+
bits.append("key `%s`" % item.key)
|
|
129
|
+
if item.headers:
|
|
130
|
+
bits.append("headers `%s`" % item.headers)
|
|
131
|
+
return "; ".join(bits) + "."
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def consumer_doc(item: kafka.Subscription) -> str:
|
|
135
|
+
group = item.client.config.get("consumer group")
|
|
136
|
+
suffix = "; consumer group `%s`" % group if group else ""
|
|
137
|
+
return "Subscribed with %s%s." % (item.client.library, suffix)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def handoff(topic: str, message: str, direction: str) -> Dict[str, str]:
|
|
141
|
+
return {
|
|
142
|
+
"kind": "message",
|
|
143
|
+
"transport": "kafka",
|
|
144
|
+
"channel": topic,
|
|
145
|
+
"message": message,
|
|
146
|
+
"direction": direction,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def publish_flow(context: str, service: str, svc_id: str, item: kafka.Publish, index: int) -> Dict[str, Any]:
|
|
151
|
+
flow_slug = slug("%s-kafka-publish-%s-%s-%d" % (service, item.topic, item.message, index))
|
|
152
|
+
broker = "kafka-" + slug(item.topic)
|
|
153
|
+
return catalog.flow(
|
|
154
|
+
"flow." + flow_slug,
|
|
155
|
+
flow_slug,
|
|
156
|
+
"Publish %s" % title(item.message),
|
|
157
|
+
"Publishes `%s` to Kafka topic `%s`." % (item.message, item.topic),
|
|
158
|
+
item.module.rel,
|
|
159
|
+
context,
|
|
160
|
+
[
|
|
161
|
+
catalog.participant(svc_id, "service", context),
|
|
162
|
+
catalog.participant(broker, "broker", None, "Kafka · " + item.topic),
|
|
163
|
+
],
|
|
164
|
+
[
|
|
165
|
+
catalog.step(
|
|
166
|
+
"publish",
|
|
167
|
+
svc_id,
|
|
168
|
+
broker,
|
|
169
|
+
"call",
|
|
170
|
+
"publish " + item.message,
|
|
171
|
+
catalog.DECLARED,
|
|
172
|
+
note=kafka.config_note(item.client),
|
|
173
|
+
line=item.line,
|
|
174
|
+
handoff=handoff(item.topic, item.message, "send"),
|
|
175
|
+
)
|
|
176
|
+
],
|
|
177
|
+
trigger={"kind": "unproven", "label": "source caller not proven", "confidence": "low"},
|
|
178
|
+
entrypoint=item.entrypoint,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def receive_flow(context: str, service: str, svc_id: str, item: kafka.Subscription, message: str, index: int) -> Dict[str, Any]:
|
|
183
|
+
flow_slug = slug("%s-kafka-consume-%s-%d" % (service, item.topic, index))
|
|
184
|
+
broker = "kafka-" + slug(item.topic)
|
|
185
|
+
return catalog.flow(
|
|
186
|
+
"flow." + flow_slug,
|
|
187
|
+
flow_slug,
|
|
188
|
+
"Consume from %s" % item.topic,
|
|
189
|
+
"Consumes Kafka messages from `%s`." % item.topic,
|
|
190
|
+
item.module.rel,
|
|
191
|
+
context,
|
|
192
|
+
[
|
|
193
|
+
catalog.participant(broker, "broker", None, "Kafka · " + item.topic),
|
|
194
|
+
catalog.participant(svc_id, "service", context),
|
|
195
|
+
],
|
|
196
|
+
[
|
|
197
|
+
catalog.step(
|
|
198
|
+
"receive",
|
|
199
|
+
broker,
|
|
200
|
+
svc_id,
|
|
201
|
+
"call",
|
|
202
|
+
"receive message",
|
|
203
|
+
catalog.DECLARED,
|
|
204
|
+
note=consumer_doc(item),
|
|
205
|
+
line=item.line,
|
|
206
|
+
handoff=handoff(item.topic, message, "receive"),
|
|
207
|
+
)
|
|
208
|
+
],
|
|
209
|
+
trigger={"kind": "message", "label": "Kafka · " + item.topic, "confidence": "high"},
|
|
210
|
+
entrypoint=item.entrypoint,
|
|
211
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Framework-neutral Kafka extraction contracts."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
9
|
+
sys.path.insert(1, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "pyplugin"))
|
|
10
|
+
|
|
11
|
+
from extract import extract # noqa: E402
|
|
12
|
+
from options import Options # noqa: E402
|
|
13
|
+
from protocol import Builder, Input # noqa: E402
|
|
14
|
+
|
|
15
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
16
|
+
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PythonKafka(unittest.TestCase):
|
|
20
|
+
def setUp(self):
|
|
21
|
+
fixture = os.path.join(HERE, "testdata", "service")
|
|
22
|
+
builder = Builder()
|
|
23
|
+
extract(
|
|
24
|
+
Input(root=os.path.relpath(fixture, ROOT), commit="abc1234", generated_at="2026-09-08T00:00:00Z"),
|
|
25
|
+
Options.of({"context": "shop", "service": "orders", "settings": "config.settings"}),
|
|
26
|
+
builder,
|
|
27
|
+
cwd=ROOT,
|
|
28
|
+
)
|
|
29
|
+
self.warnings = builder.warnings
|
|
30
|
+
self.contents = builder.files[0].contents
|
|
31
|
+
self.fragment = json.loads(self.contents)
|
|
32
|
+
self.service = self.fragment["contexts"][0]["services"][0]
|
|
33
|
+
|
|
34
|
+
def test_three_standard_clients_form_message_channels(self):
|
|
35
|
+
channels = {item["address"]: item for item in self.service["channels"]}
|
|
36
|
+
self.assertEqual(sorted(channels), ["audit.records", "orders.created", "payments.accepted"])
|
|
37
|
+
self.assertTrue(all(item["kind"] == "message" for item in channels.values()))
|
|
38
|
+
self.assertEqual(
|
|
39
|
+
[message["direction"] for message in channels["orders.created"]["messages"]],
|
|
40
|
+
["send", "receive"],
|
|
41
|
+
)
|
|
42
|
+
self.assertIn("consumer group `orders-indexer`", channels["orders.created"]["messages"][1]["doc"])
|
|
43
|
+
|
|
44
|
+
def test_client_configuration_is_kept_but_credentials_never_are(self):
|
|
45
|
+
orders = next(item for item in self.service["channels"] if item["address"] == "orders.created")
|
|
46
|
+
self.assertIn("brokers: kafka:9092", orders["doc"])
|
|
47
|
+
self.assertIn("idempotence: enabled", orders["doc"])
|
|
48
|
+
self.assertIn("security protocol: SASL_SSL", orders["doc"])
|
|
49
|
+
self.assertNotIn("must-not-leak", self.contents)
|
|
50
|
+
self.assertNotIn("sasl.password", self.contents)
|
|
51
|
+
self.assertNotIn("sasl.username", self.contents)
|
|
52
|
+
|
|
53
|
+
def test_publish_and_receive_flows_have_kafka_handoffs_and_source(self):
|
|
54
|
+
steps = [step for flow in self.fragment["flows"] for step in flow["steps"]]
|
|
55
|
+
handoffs = [step["handoff"] for step in steps]
|
|
56
|
+
self.assertTrue(any(item == {"kind": "message", "transport": "kafka", "channel": "payments.accepted", "message": "payment", "direction": "send"} for item in handoffs))
|
|
57
|
+
self.assertTrue(any(item == {"kind": "message", "transport": "kafka", "channel": "payments.accepted", "message": "payment", "direction": "receive"} for item in handoffs))
|
|
58
|
+
self.assertTrue(any(item["channel"] == "orders.created" and item["direction"] == "receive" for item in handoffs))
|
|
59
|
+
self.assertTrue(all(step["line"].endswith(tuple(str(n) for n in range(1, 100))) for step in steps))
|
|
60
|
+
|
|
61
|
+
def test_a_dynamic_topic_is_reported_and_not_invented(self):
|
|
62
|
+
self.assertEqual(len(self.warnings), 1)
|
|
63
|
+
self.assertIn("Kafka topic is dynamic", self.warnings[0].message)
|
|
64
|
+
self.assertIn("runtime_topic", self.warnings[0].message)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
unittest.main()
|