@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,601 @@
|
|
|
1
|
+
"""One Django service in, one fragment out - two, when it keeps a database.
|
|
2
|
+
|
|
3
|
+
A fragment, not a catalog: it carries one context and one service, names peers
|
|
4
|
+
it does not own, and is merged with everything else before anything validates
|
|
5
|
+
it.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import ast
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
from typing import Any, Dict, List
|
|
14
|
+
|
|
15
|
+
import apps as apps_module
|
|
16
|
+
import auth as auth_module
|
|
17
|
+
import catalog
|
|
18
|
+
import clients as clients_module
|
|
19
|
+
import contracts
|
|
20
|
+
import database
|
|
21
|
+
import domain
|
|
22
|
+
import events as events_module
|
|
23
|
+
import flows
|
|
24
|
+
import lifecycle
|
|
25
|
+
import operations
|
|
26
|
+
import routing
|
|
27
|
+
import serializers as serializers_module
|
|
28
|
+
import store as store_module
|
|
29
|
+
import transport
|
|
30
|
+
from ids import service_id, slug, title
|
|
31
|
+
from options import Options
|
|
32
|
+
from protocol import Builder, Input
|
|
33
|
+
from source import Project, const_str, dotted, keyword, read
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def extract(input_: Input, opts: Options, b: Builder, cwd: str = "") -> None:
|
|
37
|
+
cwd = cwd or os.getcwd()
|
|
38
|
+
root = os.path.abspath(os.path.join(cwd, input_.root))
|
|
39
|
+
|
|
40
|
+
def rel(path: str) -> str:
|
|
41
|
+
return os.path.relpath(path, cwd).replace(os.sep, "/")
|
|
42
|
+
|
|
43
|
+
source = os.path.normpath(os.path.join(root, opts.source or "."))
|
|
44
|
+
context = opts.context or os.path.basename(root)
|
|
45
|
+
service = opts.service or os.path.basename(root)
|
|
46
|
+
svc_id = service_id(context, service)
|
|
47
|
+
|
|
48
|
+
project = Project(root, source, rel)
|
|
49
|
+
for path, message in project.broken:
|
|
50
|
+
b.warn(path, "cannot be parsed, so nothing in it is read: %s" % message)
|
|
51
|
+
|
|
52
|
+
applications = apps_module.discover(project, list(opts.apps))
|
|
53
|
+
if not applications:
|
|
54
|
+
b.warn(svc_id, "no Django application under %s: a directory with a models module is what this reads" % rel(source))
|
|
55
|
+
|
|
56
|
+
models_by_app = {app.dotted: domain.read_models(app) for app in applications}
|
|
57
|
+
concrete_models = [model for models in models_by_app.values() for model in models if model.concrete]
|
|
58
|
+
configured_databases = database.databases_of(project, opts.settings)
|
|
59
|
+
primary_database = next((item for item in configured_databases if item.alias == "default"), None)
|
|
60
|
+
effective_store = opts.store
|
|
61
|
+
if not effective_store and concrete_models and primary_database is not None and primary_database.kind:
|
|
62
|
+
effective_store = database.store_slug(primary_database.kind)
|
|
63
|
+
store_aliases = {item.alias: (effective_store if item.alias == "default" else slug(item.alias)) for item in configured_databases}
|
|
64
|
+
|
|
65
|
+
aggregates = domain.read_aggregates(
|
|
66
|
+
project,
|
|
67
|
+
applications,
|
|
68
|
+
svc_id,
|
|
69
|
+
dict(opts.aggregates),
|
|
70
|
+
b,
|
|
71
|
+
models_by_app,
|
|
72
|
+
)
|
|
73
|
+
route_table = routing.read(project, opts.settings)
|
|
74
|
+
endpoint_apps = routed_applications(project, applications, route_table)
|
|
75
|
+
endpoints = []
|
|
76
|
+
for app in endpoint_apps:
|
|
77
|
+
endpoints += [(app, endpoint) for endpoint in transport.read_endpoints(app, b, route_table)]
|
|
78
|
+
serializer_registry = serializers_module.read(project, endpoint_apps)
|
|
79
|
+
auth_registry = auth_module.Registry(project, opts.settings, b)
|
|
80
|
+
|
|
81
|
+
known_events: Dict[str, Any] = {}
|
|
82
|
+
use_cases: List[operations.UseCase] = []
|
|
83
|
+
clients: List[clients_module.Client] = []
|
|
84
|
+
for agg in aggregates:
|
|
85
|
+
found, registry = events_module.read_events(agg, service, b)
|
|
86
|
+
agg.aggregate["events"] = found
|
|
87
|
+
known_events.update(registry)
|
|
88
|
+
use_cases += operations.read_use_cases(agg, b)
|
|
89
|
+
clients += clients_module.read_clients(agg.app, dict(opts.peers), rel, b)
|
|
90
|
+
|
|
91
|
+
reader = flows.FlowReader(
|
|
92
|
+
flows.Options(
|
|
93
|
+
context=context,
|
|
94
|
+
svc_id=svc_id,
|
|
95
|
+
service=service,
|
|
96
|
+
store=effective_store,
|
|
97
|
+
stores=store_aliases,
|
|
98
|
+
peers=dict(opts.peers),
|
|
99
|
+
events=dict(opts.events),
|
|
100
|
+
flow_wrappers=dict(opts.flow_wrappers),
|
|
101
|
+
settings=opts.settings,
|
|
102
|
+
),
|
|
103
|
+
project,
|
|
104
|
+
aggregates,
|
|
105
|
+
concrete_models + serializer_registry.models,
|
|
106
|
+
use_cases,
|
|
107
|
+
clients,
|
|
108
|
+
known_events,
|
|
109
|
+
rel,
|
|
110
|
+
b,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
found_flows = []
|
|
114
|
+
exposed: Dict[str, List[str]] = {}
|
|
115
|
+
for _app, endpoint in endpoints:
|
|
116
|
+
serializer = serializer_registry.for_endpoint(endpoint)
|
|
117
|
+
model = serializer_registry.model_for_endpoint(endpoint) if isinstance(endpoint.node, ast.ClassDef) else None
|
|
118
|
+
flow = reader.endpoint_flow(endpoint, serializer, model)
|
|
119
|
+
if flow is not None:
|
|
120
|
+
found_flows.append(flow)
|
|
121
|
+
for key in endpoint.use_cases:
|
|
122
|
+
exposed.setdefault(key, []).append(endpoint.id)
|
|
123
|
+
for agg in aggregates:
|
|
124
|
+
for module, node, decorator in transport.receivers(agg.app):
|
|
125
|
+
flow = reader.policy_flow(agg, module, node, decorator)
|
|
126
|
+
if flow is not None:
|
|
127
|
+
found_flows.append(flow)
|
|
128
|
+
found_flows.extend(reader.task_flows())
|
|
129
|
+
|
|
130
|
+
for agg in aggregates:
|
|
131
|
+
life = lifecycle.read(agg, known_events, b)
|
|
132
|
+
if life is not None:
|
|
133
|
+
agg.aggregate["lifecycle"] = life
|
|
134
|
+
for use_case in use_cases:
|
|
135
|
+
if use_case.app is not agg.app:
|
|
136
|
+
continue
|
|
137
|
+
agg.aggregate["operations"].append(operations.operation(use_case, exposed.get(use_case.key)))
|
|
138
|
+
agg.aggregate["operations"].sort(key=lambda o: o["id"])
|
|
139
|
+
for event in agg.aggregate["events"]:
|
|
140
|
+
if event["id"] not in reader.referenced:
|
|
141
|
+
b.warn(event["id"], "no flow reaches this event: nothing this extractor could follow publishes it")
|
|
142
|
+
# Where the code puts the event on the wire is its channel, when
|
|
143
|
+
# the dataclass did not say; when both say and disagree, one of
|
|
144
|
+
# the two is stale, and the dataclass's claim stays on the page.
|
|
145
|
+
for address, line in reader.produced.get(event["id"], []):
|
|
146
|
+
declared = event.get("wire", {}).get("channel", "")
|
|
147
|
+
if not declared:
|
|
148
|
+
event.setdefault("wire", {})["channel"] = address
|
|
149
|
+
elif declared != address:
|
|
150
|
+
b.warn(event["id"], "declares channel %s but is put on %s at %s" % (declared, address, line))
|
|
151
|
+
|
|
152
|
+
readme_path = os.path.join(root, "README.md")
|
|
153
|
+
readme = read(readme_path).strip() if os.path.isfile(readme_path) else ""
|
|
154
|
+
schema_name = schema_title(route_table)
|
|
155
|
+
display_name = opts.service_name or service_name_from_schema(schema_name) or readme_title(readme) or title(service)
|
|
156
|
+
|
|
157
|
+
openapi_name = opts.openapi_out or "openapi.inferred.yaml"
|
|
158
|
+
openapi_source = generated_source(input_, root, rel, openapi_name)
|
|
159
|
+
service_obj = {
|
|
160
|
+
"id": svc_id,
|
|
161
|
+
"slug": service,
|
|
162
|
+
"name": display_name,
|
|
163
|
+
"repo": opts.repo or project_repo(root),
|
|
164
|
+
"path": rel(root),
|
|
165
|
+
"readme": readme,
|
|
166
|
+
"provides": http_contracts(endpoints, svc_id, openapi_source),
|
|
167
|
+
"consumes": reader.consumes(),
|
|
168
|
+
"aggregates": [agg.aggregate for agg in aggregates],
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fragment = {
|
|
172
|
+
"generatedAt": input_.generated_at,
|
|
173
|
+
"commit": input_.commit,
|
|
174
|
+
"contexts": [
|
|
175
|
+
{
|
|
176
|
+
"id": context,
|
|
177
|
+
"slug": context,
|
|
178
|
+
"name": opts.context_name or title(context),
|
|
179
|
+
"summary": opts.context_summary or "",
|
|
180
|
+
"services": [service_obj],
|
|
181
|
+
}
|
|
182
|
+
],
|
|
183
|
+
"defs": {},
|
|
184
|
+
"flows": found_flows,
|
|
185
|
+
"adrs": [],
|
|
186
|
+
}
|
|
187
|
+
if opts.classification:
|
|
188
|
+
fragment["contexts"][0]["classification"] = opts.classification
|
|
189
|
+
b.files.append(dump(opts.out or "domain.json", fragment))
|
|
190
|
+
if service_obj["provides"]:
|
|
191
|
+
b.files.append(dump(openapi_name, openapi_document(
|
|
192
|
+
endpoints,
|
|
193
|
+
display_name,
|
|
194
|
+
b,
|
|
195
|
+
serializer_registry,
|
|
196
|
+
"" if opts.service_name else schema_name,
|
|
197
|
+
auth_registry,
|
|
198
|
+
)))
|
|
199
|
+
|
|
200
|
+
if route_table.runtime_schema and service_obj["provides"]:
|
|
201
|
+
b.warn(
|
|
202
|
+
route_table.runtime_schema,
|
|
203
|
+
"OpenAPI/Swagger is generated at runtime; the emitted OpenAPI document is inferred from URLConf, DRF declarations, schema decorators and handler expressions, so details assembled only at runtime remain unavailable without a checked-in document",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if not effective_store:
|
|
207
|
+
b.warn(
|
|
208
|
+
svc_id,
|
|
209
|
+
"the project has ORM models, but DATABASES['default'] cannot be read and no `store` option names their database",
|
|
210
|
+
)
|
|
211
|
+
return
|
|
212
|
+
kind, engine, implied = database.store_kind(project, opts.settings, opts.store_kind)
|
|
213
|
+
if implied:
|
|
214
|
+
b.warn(svc_id, "the manifest says storeKind %s but the settings' DATABASES engine is %s, which is %s; the manifest's kind is used" % (kind, engine, implied))
|
|
215
|
+
tables = store_module.read_all(
|
|
216
|
+
models_by_app,
|
|
217
|
+
aggregates,
|
|
218
|
+
svc_id,
|
|
219
|
+
effective_store,
|
|
220
|
+
kind,
|
|
221
|
+
database.default_auto_field(project, opts.settings),
|
|
222
|
+
b,
|
|
223
|
+
)
|
|
224
|
+
store_id = "%s.%s" % (svc_id, effective_store)
|
|
225
|
+
stores_fragment = {
|
|
226
|
+
"generatedAt": input_.generated_at,
|
|
227
|
+
"commit": input_.commit,
|
|
228
|
+
"contexts": [
|
|
229
|
+
{
|
|
230
|
+
"id": context,
|
|
231
|
+
"slug": context,
|
|
232
|
+
"name": "",
|
|
233
|
+
"summary": "",
|
|
234
|
+
"services": [
|
|
235
|
+
{
|
|
236
|
+
"id": svc_id,
|
|
237
|
+
"slug": service,
|
|
238
|
+
"name": "",
|
|
239
|
+
"repo": "",
|
|
240
|
+
"path": "",
|
|
241
|
+
"readme": "",
|
|
242
|
+
"provides": [],
|
|
243
|
+
"consumes": [],
|
|
244
|
+
"aggregates": [],
|
|
245
|
+
"stores": [store_id],
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"defs": {},
|
|
251
|
+
"flows": [],
|
|
252
|
+
"adrs": [],
|
|
253
|
+
"stores": [
|
|
254
|
+
catalog.store(
|
|
255
|
+
store_id,
|
|
256
|
+
effective_store,
|
|
257
|
+
opts.store_name or title(service) + " database",
|
|
258
|
+
kind,
|
|
259
|
+
svc_id,
|
|
260
|
+
tables,
|
|
261
|
+
rel(source),
|
|
262
|
+
)
|
|
263
|
+
],
|
|
264
|
+
}
|
|
265
|
+
b.files.append(dump(opts.stores_out or "stores.json", stores_fragment))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def dump(name: str, fragment: Dict[str, Any]):
|
|
269
|
+
import json
|
|
270
|
+
|
|
271
|
+
from protocol import File
|
|
272
|
+
|
|
273
|
+
return File(name=name, contents=json.dumps(fragment, indent=2, ensure_ascii=False) + "\n")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def generated_source(input_: Input, root: str, rel, name: str) -> str:
|
|
277
|
+
"""Repository-relative location at which the host writes ``name``.
|
|
278
|
+
|
|
279
|
+
New hosts tell plugins the exact output directory. The conventional
|
|
280
|
+
``<service>/portolan`` fallback keeps direct and older hosts useful.
|
|
281
|
+
"""
|
|
282
|
+
output = input_.output.strip("/")
|
|
283
|
+
if output:
|
|
284
|
+
return "%s/%s" % (output, name)
|
|
285
|
+
return rel(os.path.join(root, "portolan", name))
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def http_contracts(endpoints, svc_id: str, source: str) -> List[Dict[str, Any]]:
|
|
289
|
+
"""Interfaces the service answers on, grouped by Django application.
|
|
290
|
+
|
|
291
|
+
A runtime Swagger generator proves that a document can be served, not that
|
|
292
|
+
a static document exists for this process to read. URLConf plus the view
|
|
293
|
+
declarations still prove the operation and its route, which is enough for
|
|
294
|
+
a useful partial contract and is labelled as inferred by its Python source.
|
|
295
|
+
"""
|
|
296
|
+
grouped: Dict[str, List[Any]] = {}
|
|
297
|
+
apps: Dict[str, Any] = {}
|
|
298
|
+
for app, endpoint in endpoints:
|
|
299
|
+
if not endpoint.verb or not endpoint.path:
|
|
300
|
+
continue
|
|
301
|
+
grouped.setdefault(app.dotted, []).append(endpoint)
|
|
302
|
+
apps[app.dotted] = app
|
|
303
|
+
out = []
|
|
304
|
+
for dotted_name in sorted(grouped):
|
|
305
|
+
app = apps[dotted_name]
|
|
306
|
+
methods = []
|
|
307
|
+
seen = set()
|
|
308
|
+
for endpoint in sorted(grouped[dotted_name], key=lambda item: (item.id, item.verb, item.path)):
|
|
309
|
+
name = endpoint.id
|
|
310
|
+
if name in seen:
|
|
311
|
+
name = "%s_%s" % (name, slug(endpoint.path))
|
|
312
|
+
seen.add(name)
|
|
313
|
+
method = {"name": name}
|
|
314
|
+
if endpoint.doc:
|
|
315
|
+
method["doc"] = endpoint.doc
|
|
316
|
+
method["http"] = {"method": endpoint.verb, "path": endpoint.path}
|
|
317
|
+
methods.append(method)
|
|
318
|
+
if not methods:
|
|
319
|
+
continue
|
|
320
|
+
out.append({"id": "%s.%s" % (svc_id, slug(app.label)), "methods": methods, "source": source})
|
|
321
|
+
return out
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def openapi_document(endpoints, service_name: str, b: Builder, serializer_registry=None, api_title: str = "", auth_registry=None) -> Dict[str, Any]:
|
|
325
|
+
"""A conservative OpenAPI view over facts Django declares statically.
|
|
326
|
+
|
|
327
|
+
Routes and verbs are evidence. A bound DRF serializer and generic view add
|
|
328
|
+
the payload and framework response semantics; other operations retain an
|
|
329
|
+
explicitly unknown default response.
|
|
330
|
+
"""
|
|
331
|
+
paths: Dict[str, Dict[str, Any]] = {}
|
|
332
|
+
tags = set()
|
|
333
|
+
operation_ids = set()
|
|
334
|
+
for app, endpoint in sorted(endpoints, key=lambda item: (item[1].path, item[1].verb, item[0].label, item[1].id)):
|
|
335
|
+
if not endpoint.path or not endpoint.verb:
|
|
336
|
+
continue
|
|
337
|
+
method = endpoint.verb.lower()
|
|
338
|
+
path_item = paths.setdefault(endpoint.path, {})
|
|
339
|
+
if method in path_item:
|
|
340
|
+
b.warn(endpoint.route_source, "%s %s is declared more than once; the first route is kept in inferred OpenAPI" % (endpoint.verb, endpoint.path))
|
|
341
|
+
continue
|
|
342
|
+
tag = app.label
|
|
343
|
+
tags.add(tag)
|
|
344
|
+
operation_id = slug("%s-%s" % (tag, endpoint.id)).replace("-", "_")
|
|
345
|
+
base = operation_id
|
|
346
|
+
suffix = 2
|
|
347
|
+
while operation_id in operation_ids:
|
|
348
|
+
operation_id = "%s_%d" % (base, suffix)
|
|
349
|
+
suffix += 1
|
|
350
|
+
operation_ids.add(operation_id)
|
|
351
|
+
summary = endpoint.doc.strip().splitlines()[0] if endpoint.doc.strip() else title(endpoint.action)
|
|
352
|
+
operation: Dict[str, Any] = {
|
|
353
|
+
"operationId": operation_id,
|
|
354
|
+
"summary": summary,
|
|
355
|
+
"tags": [tag],
|
|
356
|
+
"responses": {
|
|
357
|
+
"default": {
|
|
358
|
+
"description": "Response schema and status are not available from static Django route analysis."
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
"x-portolan-inferred": True,
|
|
362
|
+
"x-portolan-source": endpoint.route_source,
|
|
363
|
+
}
|
|
364
|
+
if endpoint.doc.strip() and "\n" in endpoint.doc.strip():
|
|
365
|
+
operation["description"] = endpoint.doc.strip()
|
|
366
|
+
parameters = []
|
|
367
|
+
for name in dict.fromkeys(re.findall(r"\{([A-Za-z_][A-Za-z0-9_]*)\}", endpoint.path)):
|
|
368
|
+
converter = endpoint.path_parameters.get(name, "str")
|
|
369
|
+
schema: Dict[str, Any] = {"type": "integer"} if converter == "int" else {"type": "string"}
|
|
370
|
+
if converter == "uuid":
|
|
371
|
+
schema["format"] = "uuid"
|
|
372
|
+
elif converter == "slug":
|
|
373
|
+
schema["pattern"] = "^[-a-zA-Z0-9_]+$"
|
|
374
|
+
parameters.append(
|
|
375
|
+
{
|
|
376
|
+
"name": name,
|
|
377
|
+
"in": "path",
|
|
378
|
+
"required": True,
|
|
379
|
+
"description": "Inferred from Django's %s path converter." % converter if name in endpoint.path_parameters else "Type was not recoverable from the resolved Django route.",
|
|
380
|
+
"schema": schema,
|
|
381
|
+
"x-portolan-inferred": True,
|
|
382
|
+
}
|
|
383
|
+
)
|
|
384
|
+
if parameters:
|
|
385
|
+
operation["parameters"] = parameters
|
|
386
|
+
statuses = response_statuses(endpoint.node)
|
|
387
|
+
serializer = serializer_registry.for_endpoint(endpoint) if serializer_registry is not None else None
|
|
388
|
+
if serializer is not None and serializer_registry.describes_contract(endpoint):
|
|
389
|
+
attach_serializer_contract(operation, endpoint, serializer, serializer_registry, statuses)
|
|
390
|
+
elif statuses:
|
|
391
|
+
operation["responses"] = {
|
|
392
|
+
status: {
|
|
393
|
+
"description": "No content response read from the Django handler."
|
|
394
|
+
if status == "204"
|
|
395
|
+
else "Error response status read from the Django handler; its schema is unknown."
|
|
396
|
+
if not status.startswith("2")
|
|
397
|
+
else "Response status read from the Django handler; its schema is unknown."
|
|
398
|
+
}
|
|
399
|
+
for status in statuses
|
|
400
|
+
}
|
|
401
|
+
if serializer_registry is not None:
|
|
402
|
+
detail = contracts.read(endpoint, serializer_registry)
|
|
403
|
+
if detail.summary:
|
|
404
|
+
operation["summary"] = detail.summary
|
|
405
|
+
if detail.description:
|
|
406
|
+
operation["description"] = detail.description
|
|
407
|
+
if detail.operation_id:
|
|
408
|
+
operation_ids.discard(operation["operationId"])
|
|
409
|
+
operation["operationId"] = unique_operation_id(detail.operation_id, operation_ids)
|
|
410
|
+
if detail.tags:
|
|
411
|
+
operation["tags"] = detail.tags
|
|
412
|
+
tags.update(detail.tags)
|
|
413
|
+
if detail.parameters:
|
|
414
|
+
operation.setdefault("parameters", [])
|
|
415
|
+
for parameter in detail.parameters:
|
|
416
|
+
contracts.upsert_parameter(operation["parameters"], parameter)
|
|
417
|
+
if detail.request_schema is not None:
|
|
418
|
+
operation["requestBody"] = {
|
|
419
|
+
"required": True,
|
|
420
|
+
"content": {"application/json": {"schema": detail.request_schema}},
|
|
421
|
+
}
|
|
422
|
+
if detail.responses:
|
|
423
|
+
operation["responses"] = detail.responses
|
|
424
|
+
if auth_registry is not None:
|
|
425
|
+
security = auth_registry.security_for(endpoint)
|
|
426
|
+
if security.requirement is not None:
|
|
427
|
+
operation["security"] = security.requirement
|
|
428
|
+
if security.permissions:
|
|
429
|
+
operation["x-portolan-permissions"] = security.permissions
|
|
430
|
+
path_item[method] = operation
|
|
431
|
+
|
|
432
|
+
document = {
|
|
433
|
+
"openapi": "3.1.0",
|
|
434
|
+
"info": {
|
|
435
|
+
"title": api_title or "%s HTTP API" % service_name,
|
|
436
|
+
"version": "inferred",
|
|
437
|
+
"description": "Generated statically from Django URLConf, DRF declarations, schema decorators and handler expressions. Unknown details are left unspecified.",
|
|
438
|
+
},
|
|
439
|
+
"tags": [{"name": name} for name in sorted(tags)],
|
|
440
|
+
"paths": paths,
|
|
441
|
+
"x-portolan-inferred": True,
|
|
442
|
+
"x-portolan-generator": "extract-django",
|
|
443
|
+
}
|
|
444
|
+
components = serializer_registry.components() if serializer_registry is not None else {}
|
|
445
|
+
if components:
|
|
446
|
+
document["components"] = {"schemas": components}
|
|
447
|
+
if auth_registry is not None:
|
|
448
|
+
# The document-level requirement is what the settings alone decide,
|
|
449
|
+
# and the schemes are every one an operation or that default named.
|
|
450
|
+
default = auth_registry.default_security()
|
|
451
|
+
if default is not None:
|
|
452
|
+
document["security"] = default
|
|
453
|
+
if auth_registry.schemes:
|
|
454
|
+
document.setdefault("components", {})["securitySchemes"] = {name: auth_registry.schemes[name] for name in sorted(auth_registry.schemes)}
|
|
455
|
+
return document
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def unique_operation_id(preferred: str, used: set) -> str:
|
|
459
|
+
candidate = slug(preferred).replace("-", "_")
|
|
460
|
+
if candidate in used:
|
|
461
|
+
suffix = 2
|
|
462
|
+
while "%s_%d" % (candidate, suffix) in used:
|
|
463
|
+
suffix += 1
|
|
464
|
+
candidate = "%s_%d" % (candidate, suffix)
|
|
465
|
+
used.add(candidate)
|
|
466
|
+
return candidate
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def attach_serializer_contract(operation: Dict[str, Any], endpoint, serializer, registry, statuses: List[str]) -> None:
|
|
470
|
+
reference: Dict[str, Any] = {"$ref": "#/components/schemas/%s" % serializer.component}
|
|
471
|
+
verb = endpoint.verb.upper()
|
|
472
|
+
if verb in ("POST", "PUT", "PATCH"):
|
|
473
|
+
request_schema: Dict[str, Any] = dict(reference)
|
|
474
|
+
if verb == "PATCH":
|
|
475
|
+
request_schema = {"allOf": [reference], "x-portolan-partial": True}
|
|
476
|
+
operation["requestBody"] = {
|
|
477
|
+
"required": True,
|
|
478
|
+
"content": {"application/json": {"schema": request_schema}},
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if not statuses and (verb == "DELETE" or endpoint.action == "destroy"):
|
|
482
|
+
operation["responses"] = {"204": {"description": "Deleted successfully."}}
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
response_schema: Dict[str, Any] = reference
|
|
486
|
+
if registry.is_list(endpoint):
|
|
487
|
+
response_schema = {"type": "array", "items": reference}
|
|
488
|
+
statuses = statuses or (["201"] if endpoint.action == "create" else ["200"])
|
|
489
|
+
operation["responses"] = {}
|
|
490
|
+
for status in statuses:
|
|
491
|
+
success = status.startswith("2")
|
|
492
|
+
response: Dict[str, Any] = {
|
|
493
|
+
"description": (
|
|
494
|
+
"No content response read from the Django handler."
|
|
495
|
+
if status == "204"
|
|
496
|
+
else "Serialized response inferred from DRF view configuration."
|
|
497
|
+
if success
|
|
498
|
+
else "Error response status read from the Django handler; its schema is unknown."
|
|
499
|
+
),
|
|
500
|
+
}
|
|
501
|
+
if success and status != "204":
|
|
502
|
+
response["content"] = {"application/json": {"schema": response_schema}}
|
|
503
|
+
operation["responses"][status] = response
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def response_statuses(node: ast.AST) -> List[str]:
|
|
507
|
+
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
508
|
+
return []
|
|
509
|
+
out = set()
|
|
510
|
+
for call in ast.walk(node):
|
|
511
|
+
if not isinstance(call, ast.Call) or dotted(call.func).split(".")[-1] not in ("Response", "JsonResponse"):
|
|
512
|
+
continue
|
|
513
|
+
value = next((kw.value for kw in call.keywords if kw.arg == "status"), None)
|
|
514
|
+
if value is None and dotted(call.func).split(".")[-1] == "Response" and len(call.args) > 1:
|
|
515
|
+
value = call.args[1]
|
|
516
|
+
if isinstance(value, ast.Constant) and isinstance(value.value, int):
|
|
517
|
+
out.add(str(value.value))
|
|
518
|
+
continue
|
|
519
|
+
symbolic = dotted(value)
|
|
520
|
+
match = re.search(r"HTTP_(\d{3})_", symbolic)
|
|
521
|
+
out.add(match.group(1) if match else "200")
|
|
522
|
+
return sorted(out)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def routed_applications(project: Project, model_apps, routes: routing.Routes):
|
|
526
|
+
"""Django applications that expose routes, including stateless ones.
|
|
527
|
+
|
|
528
|
+
``apps.discover`` intentionally uses a models module as the application
|
|
529
|
+
boundary for domain extraction. HTTP is a different concern: health,
|
|
530
|
+
proxy and mailer applications often own URLConf and views but no model.
|
|
531
|
+
Their routes remain part of the service contract.
|
|
532
|
+
"""
|
|
533
|
+
out = {app.dotted: app for app in model_apps}
|
|
534
|
+
for route in routes.entries:
|
|
535
|
+
marker = ".views"
|
|
536
|
+
package = route.module.split(marker, 1)[0] if marker in route.module else route.module.rsplit(".", 1)[0]
|
|
537
|
+
if not package or package in out:
|
|
538
|
+
continue
|
|
539
|
+
app = apps_module.build(project, package)
|
|
540
|
+
if app is not None:
|
|
541
|
+
out[package] = app
|
|
542
|
+
return [out[name] for name in sorted(out, key=lambda name: out[name].rel)]
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def readme_title(markdown: str) -> str:
|
|
546
|
+
fence = ""
|
|
547
|
+
for line in markdown.split("\n"):
|
|
548
|
+
text = line.strip()
|
|
549
|
+
marker = "```" if text.startswith("```") else "~~~" if text.startswith("~~~") else ""
|
|
550
|
+
if marker:
|
|
551
|
+
if not fence:
|
|
552
|
+
fence = marker
|
|
553
|
+
elif fence == marker:
|
|
554
|
+
fence = ""
|
|
555
|
+
continue
|
|
556
|
+
if fence:
|
|
557
|
+
continue
|
|
558
|
+
if text.startswith("# "):
|
|
559
|
+
return text[2:].strip()
|
|
560
|
+
return ""
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def schema_title(routes: routing.Routes) -> str:
|
|
564
|
+
"""The human API title declared by drf-yasg in the root URLConf.
|
|
565
|
+
|
|
566
|
+
This is read from the AST only: importing a Django URLConf would execute
|
|
567
|
+
application code merely to name its documentation.
|
|
568
|
+
"""
|
|
569
|
+
if routes.root is None:
|
|
570
|
+
return ""
|
|
571
|
+
for node in ast.walk(routes.root.tree):
|
|
572
|
+
if not isinstance(node, ast.Call) or dotted(node.func).split(".")[-1] != "Info":
|
|
573
|
+
continue
|
|
574
|
+
value = const_str(keyword(node, "title"))
|
|
575
|
+
if not value and node.args:
|
|
576
|
+
value = const_str(node.args[0])
|
|
577
|
+
if value:
|
|
578
|
+
return value.strip()
|
|
579
|
+
return ""
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def service_name_from_schema(value: str) -> str:
|
|
583
|
+
"""Turn an API document title into the component name shown in catalog."""
|
|
584
|
+
without_suffix = re.sub(r"\s+(?:HTTP\s+)?API\s*$", "", value, flags=re.IGNORECASE).strip()
|
|
585
|
+
return without_suffix or value.strip()
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def project_repo(root: str) -> str:
|
|
589
|
+
"""`pyproject.toml`'s repository url, spelled the way go.mod spells a
|
|
590
|
+
module: host/owner/name."""
|
|
591
|
+
path = os.path.join(root, "pyproject.toml")
|
|
592
|
+
if not os.path.isfile(path):
|
|
593
|
+
return ""
|
|
594
|
+
match = re.search(r"(?im)^\s*(?:repository|Repository|Source)\s*=\s*[\"']([^\"']+)[\"']", read(path))
|
|
595
|
+
if not match:
|
|
596
|
+
return ""
|
|
597
|
+
url = match.group(1)
|
|
598
|
+
url = re.sub(r"^git\+", "", url)
|
|
599
|
+
url = re.sub(r"^(https?|ssh)://", "", url)
|
|
600
|
+
url = re.sub(r"^git@", "", url).replace(":", "/", 1)
|
|
601
|
+
return re.sub(r"\.git$", "", url).rstrip("/")
|