@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,545 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
|
|
10
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
11
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
+
"gopkg.in/yaml.v3"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
func fragment(t *testing.T) catalog.Catalog {
|
|
16
|
+
t.Helper()
|
|
17
|
+
|
|
18
|
+
resp, err := extract(
|
|
19
|
+
plugin.Input{Root: "testdata", Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"},
|
|
20
|
+
Options{Context: "billing", Service: "invoices", Spec: "openapi.yaml"},
|
|
21
|
+
)
|
|
22
|
+
if err != nil {
|
|
23
|
+
t.Fatal(err)
|
|
24
|
+
}
|
|
25
|
+
if len(resp.Files) != 1 {
|
|
26
|
+
t.Fatalf("expected one fragment, got %d files", len(resp.Files))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var out catalog.Catalog
|
|
30
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
|
|
31
|
+
t.Fatal(err)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return out
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func testDocument(t *testing.T, source string) *document {
|
|
38
|
+
t.Helper()
|
|
39
|
+
var root yaml.Node
|
|
40
|
+
if err := yaml.Unmarshal([]byte(source), &root); err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
node := root.Content[0]
|
|
44
|
+
return &document{root: node, path: "test.yaml", cache: map[string]*document{}}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func provided(t *testing.T) []catalog.RpcService {
|
|
48
|
+
t.Helper()
|
|
49
|
+
|
|
50
|
+
return fragment(t).Contexts[0].Services[0].Provides
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// A method is an object now, and every assertion below is about the names.
|
|
54
|
+
func names(methods []catalog.RpcMethod) []string {
|
|
55
|
+
out := make([]string, 0, len(methods))
|
|
56
|
+
for _, method := range methods {
|
|
57
|
+
out = append(out, method.Name)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return out
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Tags organise operations within one OpenAPI contract. They must not split a
|
|
64
|
+
// document into duplicate interfaces with duplicate message inventories.
|
|
65
|
+
func TestTagsStayWithinOneService(t *testing.T) {
|
|
66
|
+
provides := provided(t)
|
|
67
|
+
if len(provides) != 1 {
|
|
68
|
+
t.Fatalf("provides = %d, want one contract", len(provides))
|
|
69
|
+
}
|
|
70
|
+
if provides[0].ID != "billing.v2" {
|
|
71
|
+
t.Errorf("interface = %q", provides[0].ID)
|
|
72
|
+
}
|
|
73
|
+
if got := strings.Join(names(provides[0].Methods), ","); got != "raiseInvoice,GET /v2/health" {
|
|
74
|
+
t.Errorf("methods = %q", got)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
func TestSharedSchemaIsNotRepeatedAcrossTags(t *testing.T) {
|
|
79
|
+
doc := testDocument(t, `
|
|
80
|
+
openapi: 3.0.3
|
|
81
|
+
info: {title: shared, version: 1.0.0}
|
|
82
|
+
paths:
|
|
83
|
+
/orders:
|
|
84
|
+
get:
|
|
85
|
+
tags: [orders]
|
|
86
|
+
operationId: listOrders
|
|
87
|
+
responses:
|
|
88
|
+
'200':
|
|
89
|
+
content:
|
|
90
|
+
application/json:
|
|
91
|
+
schema: {$ref: '#/components/schemas/Result'}
|
|
92
|
+
/refunds:
|
|
93
|
+
get:
|
|
94
|
+
tags: [refunds]
|
|
95
|
+
operationId: listRefunds
|
|
96
|
+
responses:
|
|
97
|
+
'200':
|
|
98
|
+
content:
|
|
99
|
+
application/json:
|
|
100
|
+
schema: {$ref: '#/components/schemas/Result'}
|
|
101
|
+
components:
|
|
102
|
+
schemas:
|
|
103
|
+
Result: {type: object}
|
|
104
|
+
`)
|
|
105
|
+
|
|
106
|
+
services := rpcServices(doc, "shared.v1", "test.yaml", &plugin.Builder{})
|
|
107
|
+
if len(services) != 1 || len(services[0].Methods) != 2 || len(services[0].Messages) != 1 {
|
|
108
|
+
t.Fatalf("contract = %+v", services)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The id is the document's own title and major version, so it survives a patch
|
|
113
|
+
// release without every id in the catalog changing.
|
|
114
|
+
func TestApiIDFromTitleAndMajorVersion(t *testing.T) {
|
|
115
|
+
for _, p := range provided(t) {
|
|
116
|
+
if !strings.HasPrefix(p.ID, "billing.v2") {
|
|
117
|
+
t.Errorf("unexpected id %q", p.ID)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// A shared response is how a spec says "the same error body as everywhere
|
|
123
|
+
// else". Not following it would document the endpoint as returning nothing
|
|
124
|
+
// when it fails.
|
|
125
|
+
func TestFollowsRefsThroughSharedResponses(t *testing.T) {
|
|
126
|
+
var names []string
|
|
127
|
+
for _, p := range provided(t) {
|
|
128
|
+
if p.ID != "billing.v2" {
|
|
129
|
+
continue
|
|
130
|
+
}
|
|
131
|
+
for _, message := range p.Messages {
|
|
132
|
+
names = append(names, message.Name)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Line arrives through RaiseRequest, Error through the shared BadRequest.
|
|
137
|
+
want := "RaiseRequest,Invoice,Error,Line"
|
|
138
|
+
if strings.Join(names, ",") != want {
|
|
139
|
+
t.Errorf("messages = %v, want %s", names, want)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
func TestFieldTypesAndOptionality(t *testing.T) {
|
|
144
|
+
var request *catalog.RpcMessage
|
|
145
|
+
for _, p := range provided(t) {
|
|
146
|
+
for i := range p.Messages {
|
|
147
|
+
if p.Messages[i].Name == "RaiseRequest" {
|
|
148
|
+
request = &p.Messages[i]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if request == nil {
|
|
153
|
+
t.Fatal("no RaiseRequest message")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fields := map[string]catalog.Field{}
|
|
157
|
+
for _, field := range request.Fields {
|
|
158
|
+
fields[field.Name] = field
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if got := fields["customerId"].Type; got != "string (uuid)" {
|
|
162
|
+
t.Errorf("format belongs in the type, got %q", got)
|
|
163
|
+
}
|
|
164
|
+
if got := fields["customerId"].Doc; got != "Who is being billed." {
|
|
165
|
+
t.Errorf("a required field carries its description alone, got %q", got)
|
|
166
|
+
}
|
|
167
|
+
if got := fields["lines"].Type; got != "[]Line" {
|
|
168
|
+
t.Errorf("an array of refs, got %q", got)
|
|
169
|
+
}
|
|
170
|
+
// Which fields must be sent is the first thing a caller needs, and the
|
|
171
|
+
// catalog has nowhere else to put it.
|
|
172
|
+
if got := fields["lines"].Doc; !strings.HasPrefix(got, "Optional.") {
|
|
173
|
+
t.Errorf("an optional field should say so, got %q", got)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
func TestComposedSchemasMapsEnumsAndNullableTypes(t *testing.T) {
|
|
178
|
+
doc := testDocument(t, `
|
|
179
|
+
components:
|
|
180
|
+
schemas:
|
|
181
|
+
Base:
|
|
182
|
+
type: object
|
|
183
|
+
required: [id]
|
|
184
|
+
properties:
|
|
185
|
+
id: {type: string, format: uuid}
|
|
186
|
+
Account:
|
|
187
|
+
allOf:
|
|
188
|
+
- $ref: '#/components/schemas/Base'
|
|
189
|
+
- type: object
|
|
190
|
+
required: [labels]
|
|
191
|
+
properties:
|
|
192
|
+
labels:
|
|
193
|
+
type: object
|
|
194
|
+
additionalProperties: {type: string}
|
|
195
|
+
state:
|
|
196
|
+
type: string
|
|
197
|
+
enum: [active, disabled]
|
|
198
|
+
contact:
|
|
199
|
+
nullable: true
|
|
200
|
+
oneOf:
|
|
201
|
+
- {type: string, format: email}
|
|
202
|
+
- {type: string, format: phone}
|
|
203
|
+
`)
|
|
204
|
+
account := child(doc.root, "components", "schemas", "Account")
|
|
205
|
+
fields := map[string]catalog.Field{}
|
|
206
|
+
for _, field := range schemaFields(doc, account) {
|
|
207
|
+
fields[field.Name] = field
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if fields["id"].Type != "string (uuid)" || strings.HasPrefix(fields["id"].Doc, "Optional.") {
|
|
211
|
+
t.Errorf("allOf required field = %+v", fields["id"])
|
|
212
|
+
}
|
|
213
|
+
if fields["labels"].Type != "map[string]string" || strings.HasPrefix(fields["labels"].Doc, "Optional.") {
|
|
214
|
+
t.Errorf("map field = %+v", fields["labels"])
|
|
215
|
+
}
|
|
216
|
+
if fields["state"].Type != "string enum(active | disabled)" || !strings.HasPrefix(fields["state"].Doc, "Optional.") {
|
|
217
|
+
t.Errorf("enum field = %+v", fields["state"])
|
|
218
|
+
}
|
|
219
|
+
if fields["contact"].Type != "string (email) | string (phone) | null" {
|
|
220
|
+
t.Errorf("oneOf nullable field type = %q", fields["contact"].Type)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
func TestDiscriminatorKeepsPolymorphicMessagesVisible(t *testing.T) {
|
|
225
|
+
doc := testDocument(t, `
|
|
226
|
+
openapi: 3.1.0
|
|
227
|
+
info: {title: animals, version: 1.0.0}
|
|
228
|
+
paths:
|
|
229
|
+
/pets/{id}:
|
|
230
|
+
get:
|
|
231
|
+
operationId: getPet
|
|
232
|
+
responses:
|
|
233
|
+
'200':
|
|
234
|
+
content:
|
|
235
|
+
application/json:
|
|
236
|
+
schema: {$ref: '#/components/schemas/Pet'}
|
|
237
|
+
components:
|
|
238
|
+
schemas:
|
|
239
|
+
Pet:
|
|
240
|
+
oneOf:
|
|
241
|
+
- {$ref: '#/components/schemas/Cat'}
|
|
242
|
+
- {$ref: '#/components/schemas/Dog'}
|
|
243
|
+
discriminator:
|
|
244
|
+
propertyName: kind
|
|
245
|
+
mapping:
|
|
246
|
+
feline: '#/components/schemas/Cat'
|
|
247
|
+
Cat:
|
|
248
|
+
type: object
|
|
249
|
+
required: [kind, lives]
|
|
250
|
+
properties:
|
|
251
|
+
kind: {type: string}
|
|
252
|
+
lives: {type: integer}
|
|
253
|
+
Dog:
|
|
254
|
+
type: object
|
|
255
|
+
required: [kind, good]
|
|
256
|
+
properties:
|
|
257
|
+
kind: {type: string}
|
|
258
|
+
good: {type: boolean}
|
|
259
|
+
`)
|
|
260
|
+
services := rpcServices(doc, "animals.v1", "test.yaml", &plugin.Builder{})
|
|
261
|
+
if len(services) != 1 || len(services[0].Methods) != 1 {
|
|
262
|
+
t.Fatalf("services = %+v", services)
|
|
263
|
+
}
|
|
264
|
+
if got := services[0].Methods[0].Response; got != "Pet" {
|
|
265
|
+
t.Fatalf("response = %q, want Pet", got)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
byName := map[string]catalog.RpcMessage{}
|
|
269
|
+
for _, message := range services[0].Messages {
|
|
270
|
+
byName[message.Name] = message
|
|
271
|
+
}
|
|
272
|
+
pet, ok := byName["Pet"]
|
|
273
|
+
if !ok || pet.Discriminator == nil {
|
|
274
|
+
t.Fatalf("Pet discriminator is missing: %+v", pet)
|
|
275
|
+
}
|
|
276
|
+
if pet.Discriminator.Property != "kind" {
|
|
277
|
+
t.Errorf("property = %q", pet.Discriminator.Property)
|
|
278
|
+
}
|
|
279
|
+
if len(pet.Discriminator.Variants) != 2 {
|
|
280
|
+
t.Fatalf("variants = %+v", pet.Discriminator.Variants)
|
|
281
|
+
}
|
|
282
|
+
if got := pet.Discriminator.Variants[0]; got.Value != "feline" || got.Message != "Cat" {
|
|
283
|
+
t.Errorf("mapped variant = %+v", got)
|
|
284
|
+
}
|
|
285
|
+
if got := pet.Discriminator.Variants[1]; got.Value != "Dog" || got.Message != "Dog" {
|
|
286
|
+
t.Errorf("implicit variant = %+v", got)
|
|
287
|
+
}
|
|
288
|
+
if _, ok := byName["Cat"]; !ok {
|
|
289
|
+
t.Error("Cat concrete message is missing")
|
|
290
|
+
}
|
|
291
|
+
if _, ok := byName["Dog"]; !ok {
|
|
292
|
+
t.Error("Dog concrete message is missing")
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
func TestRelativeExternalSchemaRefsAreLoaded(t *testing.T) {
|
|
297
|
+
dir := t.TempDir()
|
|
298
|
+
common := filepath.Join(dir, "common.yaml")
|
|
299
|
+
root := filepath.Join(dir, "openapi.yaml")
|
|
300
|
+
if err := os.WriteFile(common, []byte(`components:
|
|
301
|
+
schemas:
|
|
302
|
+
External:
|
|
303
|
+
type: object
|
|
304
|
+
required: [code]
|
|
305
|
+
properties:
|
|
306
|
+
code: {type: integer, format: int64}
|
|
307
|
+
`), 0o600); err != nil {
|
|
308
|
+
t.Fatal(err)
|
|
309
|
+
}
|
|
310
|
+
if err := os.WriteFile(root, []byte(`openapi: 3.1.0
|
|
311
|
+
info: {title: external, version: 1.0.0}
|
|
312
|
+
paths:
|
|
313
|
+
/external:
|
|
314
|
+
get:
|
|
315
|
+
operationId: getExternal
|
|
316
|
+
responses:
|
|
317
|
+
'200':
|
|
318
|
+
content:
|
|
319
|
+
application/json:
|
|
320
|
+
schema: {$ref: './common.yaml#/components/schemas/External'}
|
|
321
|
+
`), 0o600); err != nil {
|
|
322
|
+
t.Fatal(err)
|
|
323
|
+
}
|
|
324
|
+
doc, err := load(root)
|
|
325
|
+
if err != nil {
|
|
326
|
+
t.Fatal(err)
|
|
327
|
+
}
|
|
328
|
+
services := rpcServices(doc, "external.v1", root, &plugin.Builder{})
|
|
329
|
+
if len(services) != 1 || len(services[0].Messages) != 1 {
|
|
330
|
+
t.Fatalf("services = %+v", services)
|
|
331
|
+
}
|
|
332
|
+
message := services[0].Messages[0]
|
|
333
|
+
if message.Name != "External" || len(message.Fields) != 1 || message.Fields[0].Type != "integer (int64)" {
|
|
334
|
+
t.Errorf("external message = %+v", message)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// The fragment describes what the service answers and nothing else; the name
|
|
339
|
+
// and readme belong to whichever source knows them.
|
|
340
|
+
func TestFragmentClaimsNothingItDoesNotKnow(t *testing.T) {
|
|
341
|
+
service := fragment(t).Contexts[0].Services[0]
|
|
342
|
+
|
|
343
|
+
if service.Name != "" || service.Readme != "" || service.Repo != "" {
|
|
344
|
+
t.Errorf("the api fragment should not name the service: %+v", service)
|
|
345
|
+
}
|
|
346
|
+
if len(service.Aggregates) != 0 {
|
|
347
|
+
t.Error("the api fragment should carry no aggregates")
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// What the operation sends and answers with. The document names both whenever
|
|
352
|
+
// the body is a $ref; a success with no body at all is answered by its status,
|
|
353
|
+
// because 204 is a real answer and an empty string reads as an unread one.
|
|
354
|
+
func TestShapesOnEitherSide(t *testing.T) {
|
|
355
|
+
byName := map[string]catalog.RpcMethod{}
|
|
356
|
+
for _, p := range provided(t) {
|
|
357
|
+
for _, method := range p.Methods {
|
|
358
|
+
byName[method.Name] = method
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
raise, ok := byName["raiseInvoice"]
|
|
363
|
+
if !ok {
|
|
364
|
+
t.Fatal("no raiseInvoice")
|
|
365
|
+
}
|
|
366
|
+
if raise.Request != "RaiseRequest" || raise.Response != "Invoice" {
|
|
367
|
+
t.Errorf("raiseInvoice sends %q and returns %q", raise.Request, raise.Response)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
health, ok := byName["GET /v2/health"]
|
|
371
|
+
if !ok {
|
|
372
|
+
t.Fatal("no health operation")
|
|
373
|
+
}
|
|
374
|
+
if health.Request != "" || health.Response != "200" {
|
|
375
|
+
t.Errorf("health sends %q and returns %q", health.Request, health.Response)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// The same document, read as a copy vendored from outside the estate: the
|
|
380
|
+
// fragment carries no context and no service, only the external and what it
|
|
381
|
+
// answers on, under the id the manifest gave it.
|
|
382
|
+
func TestExternalCarriesNoService(t *testing.T) {
|
|
383
|
+
resp, err := extract(
|
|
384
|
+
plugin.Input{Root: "testdata", Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"},
|
|
385
|
+
Options{External: "psp", ExternalName: "PSP", ExternalURL: "https://psp.example/docs", API: "psp.v1", Spec: "openapi.yaml"},
|
|
386
|
+
)
|
|
387
|
+
if err != nil {
|
|
388
|
+
t.Fatal(err)
|
|
389
|
+
}
|
|
390
|
+
var out catalog.Catalog
|
|
391
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
|
|
392
|
+
t.Fatal(err)
|
|
393
|
+
}
|
|
394
|
+
if len(out.Contexts) != 0 {
|
|
395
|
+
t.Errorf("an external's fragment names %d contexts", len(out.Contexts))
|
|
396
|
+
}
|
|
397
|
+
if len(out.Externals) != 1 {
|
|
398
|
+
t.Fatalf("externals = %+v", out.Externals)
|
|
399
|
+
}
|
|
400
|
+
ext := out.Externals[0]
|
|
401
|
+
if ext.ID != "psp" || ext.Slug != "psp" || ext.Name != "PSP" || ext.URL != "https://psp.example/docs" {
|
|
402
|
+
t.Errorf("external = %+v", ext)
|
|
403
|
+
}
|
|
404
|
+
var ids []string
|
|
405
|
+
for _, p := range ext.Provides {
|
|
406
|
+
ids = append(ids, p.ID)
|
|
407
|
+
}
|
|
408
|
+
if strings.Join(ids, ",") != "psp.v1" {
|
|
409
|
+
t.Errorf("interfaces = %v", ids)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// A dotted id would put the external inside a context nobody declared.
|
|
413
|
+
if _, err := extract(plugin.Input{Root: "testdata"}, Options{External: "shop.psp"}); err == nil {
|
|
414
|
+
t.Error("an external with a dot in its id was accepted")
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// A Swagger 2.0 document keeps its schemas under definitions, sends its body
|
|
419
|
+
// as a parameter and puts the response schema straight on the response. A
|
|
420
|
+
// generator that still writes 2.0 - swag, for one - documents a service
|
|
421
|
+
// exactly as well as one that writes 3.
|
|
422
|
+
func TestSwagger2DefinitionsAndBodyParameters(t *testing.T) {
|
|
423
|
+
resp, err := extract(
|
|
424
|
+
plugin.Input{Root: "testdata/swagger2", Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"},
|
|
425
|
+
Options{Context: "avia", Service: "aviasupp", Spec: "swagger.yaml"},
|
|
426
|
+
)
|
|
427
|
+
if err != nil {
|
|
428
|
+
t.Fatal(err)
|
|
429
|
+
}
|
|
430
|
+
var out catalog.Catalog
|
|
431
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
|
|
432
|
+
t.Fatal(err)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
byName := map[string]catalog.RpcMethod{}
|
|
436
|
+
shapes := map[string][]string{}
|
|
437
|
+
for _, p := range out.Contexts[0].Services[0].Provides {
|
|
438
|
+
for _, method := range p.Methods {
|
|
439
|
+
byName[method.Name] = method
|
|
440
|
+
}
|
|
441
|
+
for _, message := range p.Messages {
|
|
442
|
+
var fields []string
|
|
443
|
+
for _, field := range message.Fields {
|
|
444
|
+
fields = append(fields, field.Name+" "+field.Type)
|
|
445
|
+
}
|
|
446
|
+
shapes[message.Name] = fields
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
book, ok := byName["POST /book"]
|
|
451
|
+
if !ok {
|
|
452
|
+
t.Fatalf("no POST /book among %v", byName)
|
|
453
|
+
}
|
|
454
|
+
if book.Request != "book.Request" || book.Response != "book.Response" {
|
|
455
|
+
t.Errorf("book sends %q and returns %q", book.Request, book.Response)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// The body parameter is inherited from the path item, and a list of a
|
|
459
|
+
// definition is that definition with brackets.
|
|
460
|
+
search, ok := byName["search"]
|
|
461
|
+
if !ok {
|
|
462
|
+
t.Fatalf("no search among %v", byName)
|
|
463
|
+
}
|
|
464
|
+
if search.Request != "search.Request" || search.Response != "search.Offer[]" {
|
|
465
|
+
t.Errorf("search sends %q and returns %q", search.Request, search.Response)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if got := strings.Join(shapes["book.Request"], ", "); got != "offer_id string, passengers []book.Passenger" {
|
|
469
|
+
t.Errorf("book.Request = %q", got)
|
|
470
|
+
}
|
|
471
|
+
if _, ok := shapes["book.Passenger"]; !ok {
|
|
472
|
+
t.Errorf("book.Passenger, reached through book.Request, is not among the messages: %v", shapes)
|
|
473
|
+
}
|
|
474
|
+
if _, ok := shapes["search.Offer"]; !ok {
|
|
475
|
+
t.Errorf("search.Offer, the response item, is not among the messages: %v", shapes)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Nothing named: the tree says which documents this service implements and
|
|
480
|
+
// which it calls. A server generated from a document means provides; a client
|
|
481
|
+
// generated from one means a system outside the estate, named by the
|
|
482
|
+
// document's own title, unless the manifest says the api is one of ours. A
|
|
483
|
+
// document with neither beside it is reported and left alone.
|
|
484
|
+
func TestATreeSaysWhatIsImplementedAndWhatIsCalled(t *testing.T) {
|
|
485
|
+
read := func(opts Options) (catalog.Catalog, plugin.Response) {
|
|
486
|
+
t.Helper()
|
|
487
|
+
opts.Context, opts.Service = "avia", "aviasupp"
|
|
488
|
+
resp, err := extract(plugin.Input{Root: "testdata/discover", Commit: "abc1234", GeneratedAt: "2026-01-01T00:00:00Z"}, opts)
|
|
489
|
+
if err != nil {
|
|
490
|
+
t.Fatal(err)
|
|
491
|
+
}
|
|
492
|
+
var out catalog.Catalog
|
|
493
|
+
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &out); err != nil {
|
|
494
|
+
t.Fatal(err)
|
|
495
|
+
}
|
|
496
|
+
return out, resp
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
out, resp := read(Options{Peers: map[string]string{"partner-api.v2": "shop.partner"}})
|
|
500
|
+
|
|
501
|
+
var provided []string
|
|
502
|
+
for _, p := range out.Contexts[0].Services[0].Provides {
|
|
503
|
+
provided = append(provided, p.ID+" from "+p.Source)
|
|
504
|
+
}
|
|
505
|
+
if strings.Join(provided, ", ") != "aviasupp.v1 from testdata/discover/docs/swagger.yaml" {
|
|
506
|
+
t.Errorf("provides = %v", provided)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if len(out.Externals) != 1 {
|
|
510
|
+
t.Fatalf("externals = %+v", out.Externals)
|
|
511
|
+
}
|
|
512
|
+
acme := out.Externals[0]
|
|
513
|
+
if acme.ID != "acme-flights" || acme.Slug != "acme-flights" || acme.Name != "Acme Flights API" || acme.Summary != "Flights and ancillaries from Acme." || acme.URL != "https://developer.acme.example/flights" {
|
|
514
|
+
t.Errorf("external = %+v", acme)
|
|
515
|
+
}
|
|
516
|
+
if len(acme.Provides) != 1 || acme.Provides[0].ID != "acme-flights-api.v1" {
|
|
517
|
+
t.Errorf("acme provides %+v", acme.Provides)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
warned := func(substring string) bool {
|
|
521
|
+
for _, w := range resp.Warnings() {
|
|
522
|
+
if strings.Contains(w.Message, substring) {
|
|
523
|
+
return true
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return false
|
|
527
|
+
}
|
|
528
|
+
if !warned("found testdata/discover/internal/stray/openapi.yaml, and nothing beside it says") {
|
|
529
|
+
t.Errorf("the stray document was not reported; warnings = %+v", resp.Warnings())
|
|
530
|
+
}
|
|
531
|
+
if !warned("nameless/client/openapi.yaml is called from here and its document has no title") {
|
|
532
|
+
t.Errorf("the nameless document was not reported; warnings = %+v", resp.Warnings())
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// The manifest may name the system itself; and a called document whose
|
|
536
|
+
// api no peers line claims is a system outside the estate too.
|
|
537
|
+
out, _ = read(Options{Externals: map[string]string{"acme-flights-api.v1": "acme"}})
|
|
538
|
+
var ids []string
|
|
539
|
+
for _, e := range out.Externals {
|
|
540
|
+
ids = append(ids, e.ID)
|
|
541
|
+
}
|
|
542
|
+
if strings.Join(ids, ",") != "acme,partner" {
|
|
543
|
+
t.Errorf("externals = %v", ids)
|
|
544
|
+
}
|
|
545
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Package main is portolan-extract-openapi: an OpenAPI document in, a catalog
|
|
2
|
+
// fragment out.
|
|
3
|
+
//
|
|
4
|
+
// It describes one aspect of a service - what it answers - and nothing else.
|
|
5
|
+
// The aggregates come from a different extractor reading a different part of
|
|
6
|
+
// the tree, and the two meet in the merge. Neither knows the other exists,
|
|
7
|
+
// which is the whole reason each of them stays small.
|
|
8
|
+
package main
|
|
9
|
+
|
|
10
|
+
import (
|
|
11
|
+
"fmt"
|
|
12
|
+
"io"
|
|
13
|
+
"os"
|
|
14
|
+
|
|
15
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// Options are what the manifest tells the extractor: the things an OpenAPI
|
|
19
|
+
// document does not say about the estate it belongs to.
|
|
20
|
+
type Options struct {
|
|
21
|
+
Context string `json:"context"`
|
|
22
|
+
Service string `json:"service"`
|
|
23
|
+
|
|
24
|
+
// Spec is the document, relative to the input root. Left out, the
|
|
25
|
+
// extractor walks the tree for every document and reads what sits beside
|
|
26
|
+
// each one: a server generated from it means this service implements it,
|
|
27
|
+
// a client generated from it means this service calls it, and a called
|
|
28
|
+
// document names a system outside the estate unless `peers` says the api
|
|
29
|
+
// is one of ours.
|
|
30
|
+
Spec string `json:"spec,omitempty"`
|
|
31
|
+
|
|
32
|
+
// Peers says which called documents are ours, as the api id to the
|
|
33
|
+
// service that implements it: {"auth.v1": "auth.auth"}. A called document
|
|
34
|
+
// whose api is named here is that service's to describe, and is skipped.
|
|
35
|
+
Peers map[string]string `json:"peers,omitempty"`
|
|
36
|
+
|
|
37
|
+
// Externals names a called document's system when the name its title
|
|
38
|
+
// would give is not the one wanted, as the api id to the external's bare
|
|
39
|
+
// id: {"stripe.v1": "stripe"}.
|
|
40
|
+
Externals map[string]string `json:"externals,omitempty"`
|
|
41
|
+
|
|
42
|
+
// API prefixes the ids of the rpc services this produces. Left out, it is
|
|
43
|
+
// built from the document's title and major version - `auth` 1.0.0 gives
|
|
44
|
+
// `auth.v1`, and a tag called `users` gives `auth.v1.Users`.
|
|
45
|
+
API string `json:"api,omitempty"`
|
|
46
|
+
|
|
47
|
+
// External names a system outside the estate the document belongs to,
|
|
48
|
+
// when it is not one of ours: the copy vendored beside an adapter, read
|
|
49
|
+
// for what the third party answers on. Set, the fragment carries no
|
|
50
|
+
// context and no service, only the external, and `context` and `service`
|
|
51
|
+
// are not read.
|
|
52
|
+
External string `json:"external,omitempty"`
|
|
53
|
+
ExternalName string `json:"externalName,omitempty"`
|
|
54
|
+
ExternalSummary string `json:"externalSummary,omitempty"`
|
|
55
|
+
ExternalURL string `json:"externalUrl,omitempty"`
|
|
56
|
+
|
|
57
|
+
Out string `json:"out,omitempty"`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func main() {
|
|
61
|
+
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
62
|
+
fmt.Fprintln(os.Stderr, "portolan-extract-openapi:", err)
|
|
63
|
+
os.Exit(1)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func run(stdin io.Reader, stdout io.Writer) error {
|
|
68
|
+
return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
|
|
69
|
+
if req.Input.Root == "" {
|
|
70
|
+
return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return extract(req.Input, opts)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"properties": {
|
|
5
|
+
"context": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "Slug of the bounded context that owns the service this document belongs to. Defaults to the name of the input directory.",
|
|
8
|
+
"examples": ["auth"]
|
|
9
|
+
},
|
|
10
|
+
"service": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Slug of the service the document describes. Defaults to the name of the input directory.",
|
|
13
|
+
"examples": ["auth"]
|
|
14
|
+
},
|
|
15
|
+
"spec": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "The OpenAPI document, relative to the input root. Left out, the extractor walks the tree for every document and reads what sits beside each: a server generated from it means this service implements it, a client generated from it means this service calls it, and a called document names a system outside the estate unless peers says the api is one of ours.",
|
|
18
|
+
"examples": ["internal/infrastructure/transport/http/gen/openapi.yaml"]
|
|
19
|
+
},
|
|
20
|
+
"peers": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": { "type": "string" },
|
|
23
|
+
"description": "Which called documents are ours, as the api id to the service that implements it. A called document whose api is named here is that service's to describe, and is skipped rather than read as a system outside the estate.",
|
|
24
|
+
"examples": [{ "auth.v1": "auth.auth" }]
|
|
25
|
+
},
|
|
26
|
+
"externals": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": { "type": "string" },
|
|
29
|
+
"description": "What a called document's system is called when the name its title would give is not the one wanted, as the api id to the external's bare id. Left out, a document titled \"Stripe API\" makes the external stripe.",
|
|
30
|
+
"examples": [{ "stripe.v1": "stripe" }]
|
|
31
|
+
},
|
|
32
|
+
"api": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Prefix for the ids of the rpc services this produces. Left out, it is the document's own x-portolan-api when it carries one, and otherwise built from its title and major version: auth 1.0.0 gives auth.v1, and a tag called users gives auth.v1.Users.",
|
|
35
|
+
"examples": ["auth.v1"]
|
|
36
|
+
},
|
|
37
|
+
"external": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Id of a system outside the estate the document belongs to, when it is not one of ours: a copy vendored beside the adapter that calls it, read for what the third party answers on. A bare name with no dot, the same one the caller's extractor is given under externals. Set, the fragment carries the external and no service, and context and service are not read.",
|
|
40
|
+
"examples": ["stripe"]
|
|
41
|
+
},
|
|
42
|
+
"externalName": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "What the external is called on the page.",
|
|
45
|
+
"examples": ["Stripe"]
|
|
46
|
+
},
|
|
47
|
+
"externalSummary": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "A sentence or two on what the estate uses it for."
|
|
50
|
+
},
|
|
51
|
+
"externalUrl": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Where the third party documents itself, for a reader who needs more than the vendored copy.",
|
|
54
|
+
"examples": ["https://docs.stripe.com/api"]
|
|
55
|
+
},
|
|
56
|
+
"out": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"description": "Name of the fragment file, inside the step's output directory.",
|
|
59
|
+
"default": "api.json"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|