@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,386 @@
|
|
|
1
|
+
# extract-django
|
|
2
|
+
|
|
3
|
+
A Django service in, a catalog fragment out — two, when the service keeps a
|
|
4
|
+
database. The Django twin of `extract-go` and `extract-ts`, with one difference
|
|
5
|
+
that runs through all of it: those read a layout somebody keeps by hand, and
|
|
6
|
+
this reads the framework. A Django project already says where the model is,
|
|
7
|
+
what the rows are and what answers a request; nothing is annotated for the
|
|
8
|
+
catalog, and the applications are the claim.
|
|
9
|
+
|
|
10
|
+
Written in Python and run as a process plugin, `python3 plugins/extract-django/main.py`.
|
|
11
|
+
Python 3.9 or newer and nothing but the standard library: a plugin that needed
|
|
12
|
+
the project's own dependencies installed could not be run over somebody else's
|
|
13
|
+
checkout. **The project is never imported.** `django.setup()` runs the code it
|
|
14
|
+
finds, reads the environment and opens sockets, and an extractor doing any of
|
|
15
|
+
those has stopped being a pure function of the tree. Everything is read through
|
|
16
|
+
`ast`, and names are resolved by import and by file, the way `extract-ts`
|
|
17
|
+
resolves them without a type checker.
|
|
18
|
+
|
|
19
|
+
## The layout it reads
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
<app>/ an application: a package with a models module
|
|
23
|
+
models.py the root, named after the application, and its entities
|
|
24
|
+
values.py value objects: the frozen dataclasses
|
|
25
|
+
events.py events: a dataclass with a `name`, or a bare Signal
|
|
26
|
+
services.py use cases: one function per scenario
|
|
27
|
+
views.py DRF views: a ViewSet's actions, an @api_view function
|
|
28
|
+
urls.py the router registration, which names the endpoints
|
|
29
|
+
handlers.py policies: `@receiver(signal)`, also read from signals.py
|
|
30
|
+
tasks.py Celery tasks: what runs later; an enqueue of one is a hop
|
|
31
|
+
clients/<peer>/
|
|
32
|
+
openapi.yaml the peer's document, vendored
|
|
33
|
+
client.py the class that calls it
|
|
34
|
+
apps.py the AppConfig, read for the label half of a table name
|
|
35
|
+
README.md the aggregate's page, or the docstring of the root
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`models.py` may be a `models/` package, and so may every other module here.
|
|
39
|
+
|
|
40
|
+
## What becomes what
|
|
41
|
+
|
|
42
|
+
**Aggregate.** One per application. The root is the model named after the
|
|
43
|
+
application — `invoices` holds `Invoice` — or the only model there is, or the
|
|
44
|
+
one `aggregates` names in the manifest; an application with several models and
|
|
45
|
+
no such name is reported and skipped, because guessing which of them the others
|
|
46
|
+
hang off is how an aggregate boundary gets drawn wrong and stays wrong. The
|
|
47
|
+
aggregate is named and slugged after the root, so a Django `invoices` and a Go
|
|
48
|
+
`invoice` package land on the same id. Every concrete model of the application
|
|
49
|
+
is an entity, the root included and first; abstract models are not. The
|
|
50
|
+
readme is `README.md` in the application, or the root's docstring.
|
|
51
|
+
|
|
52
|
+
**Field.** Each model attribute assigned a field, with the type as written:
|
|
53
|
+
`CharField`, `DateTimeField`, and a relation as `ForeignKey[Invoice]`. The doc
|
|
54
|
+
is the field's own `help_text`, which is the one place a Django model already
|
|
55
|
+
writes down what a column means. Value objects are the frozen dataclasses in
|
|
56
|
+
`values.py`, their fields the annotations as written.
|
|
57
|
+
|
|
58
|
+
**Event.** `events.py`, in either of the two ways a Django project says it. A
|
|
59
|
+
dataclass with `name = "billing.InvoiceIssued"` is an event, its payload the
|
|
60
|
+
annotated fields and its wire name that string; `channel` beside it is where it
|
|
61
|
+
goes out — or, when the dataclass does not say, the address the code puts it
|
|
62
|
+
on (below), and when both say and disagree that is reported, with the
|
|
63
|
+
dataclass's claim kept on the page. A module-level `Signal()` is one too — leaving it out would hide a
|
|
64
|
+
publish — and it declares no payload, which is a diagnostic rather than an
|
|
65
|
+
empty shape nobody questions. A signal named after a dataclass event (
|
|
66
|
+
`invoice_issued` beside `InvoiceIssued`) is how that event travels, not a
|
|
67
|
+
second event.
|
|
68
|
+
|
|
69
|
+
**Operation.** Each public function of `services.py`, its id the function name
|
|
70
|
+
in PascalCase: `issue_invoice` → `IssueInvoice`. It is a command when it saves,
|
|
71
|
+
creates, deletes, updates or publishes — or opens a transaction — and a query
|
|
72
|
+
when it does not. The doc is the function's docstring, first paragraph.
|
|
73
|
+
`exposedBy` names the endpoints that run it.
|
|
74
|
+
|
|
75
|
+
**Endpoint and inferred HTTP contract.** The root URLConf is found through
|
|
76
|
+
`DJANGO_SETTINGS_MODULE` and `ROOT_URLCONF`; nested `include()` prefixes,
|
|
77
|
+
`path`/`re_path`, DRF router registrations, concrete generic views and local
|
|
78
|
+
HTTP handlers are then followed without importing the project. A `ViewSet`'s
|
|
79
|
+
inherited actions and every `@action` it adds are included. The id is the
|
|
80
|
+
router basename and action: `router.register("invoices", InvoiceViewSet,
|
|
81
|
+
basename="invoice")` makes `invoice_issue` at the collection or detail route.
|
|
82
|
+
|
|
83
|
+
Those operations become a partial `provides` interface and an
|
|
84
|
+
`openapi.inferred.yaml` OpenAPI 3.1 document even when Swagger is generated
|
|
85
|
+
only at runtime. The normal API reference can therefore render the result. It
|
|
86
|
+
is partial deliberately and carries `x-portolan-inferred`: URLConf proves the
|
|
87
|
+
verb and path, while request and response schemas and status codes stay absent
|
|
88
|
+
unless source code proves them. A checked-in document remains
|
|
89
|
+
`extract-openapi`'s richer source of truth. Stateless Django applications with
|
|
90
|
+
routes but no models contribute to this HTTP contract without being invented
|
|
91
|
+
as domain aggregates.
|
|
92
|
+
|
|
93
|
+
**DRF payload schemas.** A `Serializer` becomes an OpenAPI object from its
|
|
94
|
+
declared fields. A `ModelSerializer` joins `Meta.fields` to the named model's
|
|
95
|
+
Django fields and honours explicit overrides and `read_only_fields`.
|
|
96
|
+
Inheritance and nested serializers (including `many=True`) become component
|
|
97
|
+
references. The field mapping carries scalar formats, nullability, length,
|
|
98
|
+
help text, defaults and read/write direction where syntax proves them.
|
|
99
|
+
|
|
100
|
+
An endpoint is joined to `serializer_class`, an action's own
|
|
101
|
+
`serializer_class`, or a statically decidable `get_serializer_class()` branch.
|
|
102
|
+
Standard generic actions supply list/object response shapes and the normal
|
|
103
|
+
`200`, `201` and `204` statuses. A concrete `Response` or `JsonResponse`
|
|
104
|
+
supplies the statuses written in its handler. Operations without that evidence
|
|
105
|
+
keep an unknown default response rather than borrowing a nearby serializer.
|
|
106
|
+
|
|
107
|
+
For custom handlers, literal response objects and lists are read recursively;
|
|
108
|
+
`Serializer(...).data` and `self.get_serializer(..., many=True).data` retain
|
|
109
|
+
their component references even when nested in an envelope such as
|
|
110
|
+
`{"data": serializer.data}`. `swagger_auto_schema` and `extend_schema` take
|
|
111
|
+
precedence where they declare request/response schemas, operation text, tags,
|
|
112
|
+
ids or manual parameters. Query parameters come from explicit
|
|
113
|
+
`request.GET.get(...)` and `request.query_params.get(...)` reads, including
|
|
114
|
+
literal defaults and simple numeric casts. `filterset_fields`, `search_fields`,
|
|
115
|
+
`ordering_fields` and an explicit pagination class add the conventional DRF
|
|
116
|
+
filter, search, ordering and page parameters. A parameter is marked required only when a missing value is
|
|
117
|
+
statically followed by a raise.
|
|
118
|
+
|
|
119
|
+
**Auth, in the contract.** Who may call an operation is read off DRF's
|
|
120
|
+
`permission_classes` and `authentication_classes`, in the order DRF applies
|
|
121
|
+
them: the action's own (`@action(permission_classes=…)`, or the decorators on
|
|
122
|
+
an `@api_view` function), then a `get_permissions()` a `self.action` branch
|
|
123
|
+
can decide, then the class attribute — the view's own or one a local base
|
|
124
|
+
class declares — then `REST_FRAMEWORK` in the settings, and past all of those
|
|
125
|
+
DRF's defaults. An authentication class is written as an OpenAPI security
|
|
126
|
+
scheme under the id drf-spectacular would give it (`SessionAuthentication` →
|
|
127
|
+
`cookieAuth`, `TokenAuthentication` → `tokenAuth`, a JWT class → `jwtAuth`),
|
|
128
|
+
and the permissions decide the operation's `security`: required for
|
|
129
|
+
`IsAuthenticated` and its kin, optional (`{}` among the alternatives) for
|
|
130
|
+
`AllowAny`, and for `IsAuthenticatedOrReadOnly` required on everything but
|
|
131
|
+
`GET`, `HEAD` and `OPTIONS`. The classes as the code names them are kept under
|
|
132
|
+
`x-portolan-permissions`. `extend_schema(auth=…)` and
|
|
133
|
+
`swagger_auto_schema(security=…)` win outright when written as literals. A
|
|
134
|
+
permission the reader does not know — a project's own `IsOwner` — is not
|
|
135
|
+
guessed at: the operation carries no `security` of its own, the class is still
|
|
136
|
+
named, and the diagnostic says so; a custom authentication class becomes a
|
|
137
|
+
scheme of its own whose transport is said to be unknown.
|
|
138
|
+
|
|
139
|
+
**Lifecycle.** Read off the table the model keeps, never off the branches of
|
|
140
|
+
its methods. Either the table is a `TRANSITIONS` mapping beside the
|
|
141
|
+
`TextChoices` that names the states —
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
class Status(models.TextChoices):
|
|
145
|
+
DRAFT = "draft", "Draft"
|
|
146
|
+
ISSUED = "issued", "Issued"
|
|
147
|
+
|
|
148
|
+
TRANSITIONS = {Status.DRAFT: [Status.ISSUED], Status.ISSUED: []}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
— and the mover is the method assigning `self.status`; or the table is
|
|
152
|
+
django-fsm's `@transition(field=status, source=…, target=…)` decorators, which
|
|
153
|
+
are the same table written one edge at a time. Either way `emits` is the event
|
|
154
|
+
the method's return annotation names. The first state is where a new row
|
|
155
|
+
starts; a state nothing leads out of is terminal, which is derived on the page
|
|
156
|
+
and never written down.
|
|
157
|
+
|
|
158
|
+
**Flow, from an endpoint.** Each view action opens one: `client → service : rpc
|
|
159
|
+
<endpoint>`, then follows project-local calls through imported functions,
|
|
160
|
+
`self.method()`, `super().method()` and class or static methods until it reaches
|
|
161
|
+
observable effects. Traversal is bounded and cycles are cut by symbol, so a
|
|
162
|
+
recursive helper cannot make extraction recursive. ORM models from routed
|
|
163
|
+
applications remain visible here even when the application has no unambiguous
|
|
164
|
+
aggregate root. A URLConf may directly mount an arbitrarily named method of a
|
|
165
|
+
plain class, such as `Planet.fetch`; that is still an HTTP flow root. When no
|
|
166
|
+
decorator or conventional handler name proves one verb, the flow is retained
|
|
167
|
+
but the route is omitted from inferred OpenAPI rather than guessed. An
|
|
168
|
+
inherited DRF generic action has no local handler body, so
|
|
169
|
+
its framework behaviour is reconstructed instead: list/retrieve read the
|
|
170
|
+
model, create/update validate through the selected serializer and persist it,
|
|
171
|
+
and destroy reads then deletes it. The model must be proven by `queryset`,
|
|
172
|
+
`get_queryset()` or a serializer's `Meta.model`; a URL or class name is never
|
|
173
|
+
used to invent a persistence hop. These steps point at the view declaration
|
|
174
|
+
and say that DRF supplied them, keeping inferred framework behaviour distinct
|
|
175
|
+
from a custom handler read directly from code.
|
|
176
|
+
|
|
177
|
+
**Flow, from a policy.** Each `@receiver` opens one on the bus: `bus → service
|
|
178
|
+
: event <ref>`, where the event is the signal it is given — one of this
|
|
179
|
+
service's own, or another service's placed by the manifest's `events`. A
|
|
180
|
+
receiver on one of Django's model signals — `post_save`, `pre_delete`,
|
|
181
|
+
`m2m_changed` and the rest — opens none: that is a hook on the row, not a
|
|
182
|
+
policy on an event. It says nothing about what happened, and it fires for
|
|
183
|
+
every save, migrations and fixtures included, so it is reported, with the
|
|
184
|
+
`sender` it hangs on, as the one thing left to move onto an event.
|
|
185
|
+
|
|
186
|
+
**Inside a body.** Statements are read in source order, and a chain left to
|
|
187
|
+
right. `Invoice.objects.get(…)` is a hop into the store, and so are
|
|
188
|
+
`Invoice.objects.filter(…).first()` - where the queryset is built, which is
|
|
189
|
+
where the line is - and a `save()` or `delete()` on something the ORM handed
|
|
190
|
+
back; an event handed to anything — a signal's `send`, a project's own
|
|
191
|
+
`publish` — is the event leaving for the bus, and where the call names an
|
|
192
|
+
address — `producer.send("topic", …)`, `produce`, NATS' or Redis' `publish`,
|
|
193
|
+
Channels' `group_send`, as a literal, a module constant or a `settings.X` —
|
|
194
|
+
or is a function of the project whose body does, the step says `on <address>`;
|
|
195
|
+
a standard `requests`, `httpx` or `aiohttp` call is an unresolved rpc to the
|
|
196
|
+
URL's host, including URLs carried through local variables, settings and
|
|
197
|
+
`urljoin`; a call on a vendored client is an rpc to the peer; a `.delay()` or `.apply_async()` on a function decorated
|
|
198
|
+
`@shared_task` or `@app.task`, directly or through `.s()`, is a hop to the
|
|
199
|
+
queue it lands on, `celery-<queue>` — decided the way Celery decides it, by the
|
|
200
|
+
reader `extract-celery` shares through `pyplugin`: the `queue=` at the call,
|
|
201
|
+
then the decorator's, then `task_routes`, then `task_default_queue` — so the
|
|
202
|
+
endpoint and task flows carry the same exact queue and task wire name and are
|
|
203
|
+
composed at the worker receive step; and `transaction.on_commit(…)` around it,
|
|
204
|
+
as a lambda or a `partial`, is the note that it waits for the commit. A call into `services.py` and ordinary project helpers is followed within the same
|
|
205
|
+
bounded traversal. `if` becomes an alt when some arm holds
|
|
206
|
+
a hop, and a branch ending in a `return` or a `raise` is terminal; a `for`, a
|
|
207
|
+
`while`, a `with transaction.atomic()` and an `except` are a note on the steps
|
|
208
|
+
inside them. `await` is transparent. Every step is `declared`.
|
|
209
|
+
|
|
210
|
+
The worker receive is also a source seam. When `extract-celery` is enabled,
|
|
211
|
+
its transport flow continues into the task function body read here, so one
|
|
212
|
+
request flow can show `HTTP → enqueue → worker → database/API` without
|
|
213
|
+
repeating the enqueue. The task-body fragment is source-backed and disappears
|
|
214
|
+
as a standalone fragment once composition consumes it.
|
|
215
|
+
|
|
216
|
+
**Project wrappers.** Some repositories deliberately put a semantic boundary
|
|
217
|
+
around infrastructure or a business integration. `flowWrappers` maps that
|
|
218
|
+
callable's fully qualified Python name to the step the reader should show,
|
|
219
|
+
instead of expanding its implementation on every call:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"flowWrappers": {
|
|
224
|
+
"util.logger.send_log": {
|
|
225
|
+
"label": "Record application log",
|
|
226
|
+
"technical": true
|
|
227
|
+
},
|
|
228
|
+
"mailer.client.send": {
|
|
229
|
+
"label": "Send email",
|
|
230
|
+
"target": "messaging.mailer"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`technical: true` keeps one counted step per flow (`×N`) rather than flooding
|
|
237
|
+
every branch with the same logger, metric or tracing implementation. It does
|
|
238
|
+
not silently discard the calls: their count, wrapper name and first source
|
|
239
|
+
line remain visible. A wrapper without `technical` is a normal semantic step
|
|
240
|
+
and is kept at every call site. `target` is an optional catalog service id;
|
|
241
|
+
without it the wrapper is shown as an operation inside the current service.
|
|
242
|
+
|
|
243
|
+
**Peer.** `clients/<peer>/` holds the class that calls and the document it was
|
|
244
|
+
vendored from. The code names the verb and the route —
|
|
245
|
+
`self._http.post("/v1/quotes")`, httpx or requests alike — and the document
|
|
246
|
+
says which operation answers there, so the call is recorded under the id the
|
|
247
|
+
callee's own extractor gives it: `pricing.v1.Quotes/createQuote`, spelled by
|
|
248
|
+
the same rules as `plugins/openapi`. The peer is the manifest's `peers` entry
|
|
249
|
+
for that api id; without one the lane is `unknown` and the step unresolved.
|
|
250
|
+
Without a document there is no id to share, and the call is recorded against
|
|
251
|
+
the route it names.
|
|
252
|
+
|
|
253
|
+
**Store.** The models are the schema, so the tables come out of this plugin
|
|
254
|
+
rather than out of `extract-sql`. A Django model is both the domain object and
|
|
255
|
+
the row, which means the two facts a SQL reader has to pair up — a column, and
|
|
256
|
+
the field it carries — are one declaration here: `maps` is exact, and so is
|
|
257
|
+
`persists`, down to the block. The table is `Meta.db_table` or the name Django
|
|
258
|
+
composes from the application's label; a column is `db_column` or the field's
|
|
259
|
+
own name, `_id` for a relation; the type is what Django's PostgreSQL backend
|
|
260
|
+
emits, so `CharField(max_length=32)` is `varchar(32)` and a key into a
|
|
261
|
+
`bigserial` is a `bigint`. Indexes come from `db_index`, `unique`,
|
|
262
|
+
`Meta.indexes`, `Meta.unique_together` and `Meta.constraints`; an index Django
|
|
263
|
+
would name with a hash of its own is called after its table and columns, since
|
|
264
|
+
inventing the hash would be a name no database has.
|
|
265
|
+
|
|
266
|
+
The primary store is inferred from `DATABASES["default"]` when that setting is
|
|
267
|
+
statically readable; `store` remains the override for settings assembled at
|
|
268
|
+
runtime or for a catalog that needs a different stable slug. Every concrete,
|
|
269
|
+
non-proxy model becomes a table even when its application has several possible
|
|
270
|
+
aggregate roots: ambiguity limits the domain model, not the persistence
|
|
271
|
+
schema. Fields from abstract model bases are copied into those tables
|
|
272
|
+
regardless of which model module sorts first. `DEFAULT_AUTO_FIELD` supplies
|
|
273
|
+
implicit primary-key types. PostgreSQL `ArrayField` nesting and
|
|
274
|
+
`MultiSelectField` storage are rendered as their database types; `to_field`
|
|
275
|
+
selects a relation's referenced column, and `db_constraint=False` keeps a
|
|
276
|
+
logical relation from being drawn as a physical foreign-key constraint. An
|
|
277
|
+
implicit `ManyToManyField` contributes the join table Django creates, while an
|
|
278
|
+
explicit `through` model is already a normal model and therefore a normal
|
|
279
|
+
table. Calls routed through `.using("alias")` or `.db_manager("alias")` keep
|
|
280
|
+
that alias on the flow instead of being attributed to the primary store.
|
|
281
|
+
|
|
282
|
+
## What it does not read
|
|
283
|
+
|
|
284
|
+
Named here rather than left to be discovered: **migrations** (the models are
|
|
285
|
+
the schema; a migration is how it got there), **the bodies of Celery tasks**
|
|
286
|
+
(an enqueue is a hop to its queue, and what the worker does there is
|
|
287
|
+
`extract-celery`'s), **admin**, **templates**, **middleware**, **management
|
|
288
|
+
commands**, **signals connected outside a `@receiver`**, and a **many-to-many**
|
|
289
|
+
field with an explicit `through` model beyond the table represented by that
|
|
290
|
+
model itself.
|
|
291
|
+
|
|
292
|
+
## Options
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"context": "shop",
|
|
297
|
+
"service": "billing",
|
|
298
|
+
"store": "pg",
|
|
299
|
+
"storeKind": "postgres",
|
|
300
|
+
"aggregates": { "invoices": "Invoice" },
|
|
301
|
+
"peers": { "pricing.v1": "shop.pricing" },
|
|
302
|
+
"events": { "payments.events": "payments.ledger.payment" },
|
|
303
|
+
"source": ".",
|
|
304
|
+
"settings": "config.settings",
|
|
305
|
+
"out": "domain.json",
|
|
306
|
+
"storesOut": "stores.json"
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`source` is the directory the applications are looked for in, the input root
|
|
311
|
+
unless said otherwise; `apps` names them outright for a project that keeps them
|
|
312
|
+
somewhere a models module would not be found. `store` overrides the stable slug
|
|
313
|
+
inferred from `DATABASES["default"]`; it is required only when the settings are
|
|
314
|
+
not statically readable. `storeKind` is read off
|
|
315
|
+
`DATABASES["default"]["ENGINE"]` in the settings module when left out —
|
|
316
|
+
`postgresql`, `postgis` and `psqlextra.backend` are `postgres`, and `sqlite3`
|
|
317
|
+
is `sqlite`; given, it wins, and a disagreement with the settings is reported.
|
|
318
|
+
`settings` names the Django settings module — for the database, and
|
|
319
|
+
for the queues Celery is configured with — only where `manage.py` does not.
|
|
320
|
+
Everything else means what it means for `extract-ts`.
|
|
321
|
+
|
|
322
|
+
## Extraction limits
|
|
323
|
+
|
|
324
|
+
These cases do not become facts in the fragment:
|
|
325
|
+
|
|
326
|
+
- a domain aggregate for an application with no models, or with several and no
|
|
327
|
+
root among them named after it (its statically resolvable HTTP routes are
|
|
328
|
+
still included);
|
|
329
|
+
- a model that declares no fields;
|
|
330
|
+
- an events module holding a class with no wire name, and a signal with no
|
|
331
|
+
payload;
|
|
332
|
+
- an application with no services module;
|
|
333
|
+
- a dynamically assembled URLConf target or an unrestricted function view
|
|
334
|
+
whose HTTP method cannot be proven from syntax;
|
|
335
|
+
- serializer fields created dynamically, a `get_serializer_class()` decision
|
|
336
|
+
that is not reducible to `self.action`, and custom serializer classes that
|
|
337
|
+
do not derive from DRF `Serializer`/`ModelSerializer`;
|
|
338
|
+
- a table with an edge no method makes, a method moving into a state the table
|
|
339
|
+
lacks, a status assigned to something the states do not name, and a model
|
|
340
|
+
that moves its status while declaring no table at all;
|
|
341
|
+
- a permission class DRF does not ship, and a permission or authentication
|
|
342
|
+
list built with `|`, `&` or anything but a literal list of classes;
|
|
343
|
+
- a client with no document beside it, and a route its document does not
|
|
344
|
+
declare;
|
|
345
|
+
- an api id the manifest names no peer for;
|
|
346
|
+
- a receiver on a model signal, `post_save` and its kin, named with the model
|
|
347
|
+
it hangs on;
|
|
348
|
+
- a many-to-many field, whose join table is not named here;
|
|
349
|
+
- an event no flow reaches;
|
|
350
|
+
- a file that does not parse, by path.
|
|
351
|
+
|
|
352
|
+
## Manifest
|
|
353
|
+
|
|
354
|
+
```json
|
|
355
|
+
{
|
|
356
|
+
"plugins": [{ "name": "django-domain", "process": { "command": "python3", "args": ["plugins/extract-django/main.py"] } }],
|
|
357
|
+
"extract": [
|
|
358
|
+
{
|
|
359
|
+
"plugin": "django-domain",
|
|
360
|
+
"in": "examples/shop/billing",
|
|
361
|
+
"out": "examples/shop/billing/portolan",
|
|
362
|
+
"options": {
|
|
363
|
+
"context": "shop",
|
|
364
|
+
"service": "billing",
|
|
365
|
+
"store": "pg",
|
|
366
|
+
"peers": { "pricing.v1": "shop.pricing" },
|
|
367
|
+
"events": { "payments.events": "payments.ledger.payment" }
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Tests
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
python3 -m unittest discover -s plugins/extract-django -t plugins/extract-django -p "*_test.py"
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
The reader is held to fixtures under `testdata/`: `billing`, a service in the
|
|
381
|
+
layout above with each shape it claims to read present once, against the golden
|
|
382
|
+
`expected.json` and `expected-stores.json`; `fsm`, the same lifecycle written
|
|
383
|
+
with django-fsm; and `drift`, a table and the methods that no longer agree with
|
|
384
|
+
it, where every diagnostic is one the reader is meant to raise.
|
|
385
|
+
`UPDATE_GOLDEN=1` writes the goldens again after a deliberate change, and the
|
|
386
|
+
diff is the review.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Which directories are Django applications.
|
|
2
|
+
|
|
3
|
+
An application is a package holding `models.py` (or a `models/` package): that
|
|
4
|
+
is what Django itself looks for, and it is the smallest claim a project makes
|
|
5
|
+
about where one part of the model ends and the next begins. `apps` in the
|
|
6
|
+
manifest names them outright for a project that keeps its applications
|
|
7
|
+
somewhere this would not look.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import ast
|
|
13
|
+
import os
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from source import Module, Project, bases, dotted, read
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class App:
|
|
22
|
+
label: str # the directory's name: "invoices"
|
|
23
|
+
dotted: str # the package, as an import spells it: "billing.invoices"
|
|
24
|
+
dir: str # absolute
|
|
25
|
+
rel: str
|
|
26
|
+
models: List[Module] = field(default_factory=list)
|
|
27
|
+
modules: List[Module] = field(default_factory=list)
|
|
28
|
+
readme: str = ""
|
|
29
|
+
|
|
30
|
+
def module(self, name: str) -> Optional[Module]:
|
|
31
|
+
"""`<app>/<name>.py`, or the `__init__` of `<app>/<name>/`."""
|
|
32
|
+
for m in self.modules:
|
|
33
|
+
if m.dotted in (self.dotted + "." + name, self.dotted + "." + name + ".__init__"):
|
|
34
|
+
return m
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
def package(self, name: str) -> List[Module]:
|
|
38
|
+
"""Every module of `<app>/<name>.py` or `<app>/<name>/`."""
|
|
39
|
+
prefix = self.dotted + "." + name
|
|
40
|
+
return [m for m in self.modules if m.dotted == prefix or m.dotted.startswith(prefix + ".")]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def discover(project: Project, named: List[str]) -> List[App]:
|
|
44
|
+
"""The applications, in path order. A name in `apps` is a package, so
|
|
45
|
+
`billing.invoices` and `invoices` are both accepted."""
|
|
46
|
+
packages = []
|
|
47
|
+
if named:
|
|
48
|
+
for name in named:
|
|
49
|
+
packages.append(name)
|
|
50
|
+
else:
|
|
51
|
+
for dotted_name in sorted(project.modules):
|
|
52
|
+
if dotted_name.endswith(".models"):
|
|
53
|
+
packages.append(dotted_name[: -len(".models")])
|
|
54
|
+
elif dotted_name.endswith(".models.__init__"):
|
|
55
|
+
packages.append(dotted_name[: -len(".models.__init__")])
|
|
56
|
+
out = []
|
|
57
|
+
seen = set()
|
|
58
|
+
for package in packages:
|
|
59
|
+
if package in seen or not package:
|
|
60
|
+
continue
|
|
61
|
+
seen.add(package)
|
|
62
|
+
app = build(project, package)
|
|
63
|
+
if app is not None:
|
|
64
|
+
out.append(app)
|
|
65
|
+
return sorted(out, key=lambda a: a.rel)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def build(project: Project, package: str) -> Optional[App]:
|
|
69
|
+
directory = os.path.join(project.source, *package.split("."))
|
|
70
|
+
if not os.path.isdir(directory):
|
|
71
|
+
return None
|
|
72
|
+
app = App(label=package.split(".")[-1], dotted=package, dir=directory, rel=project.rel(directory))
|
|
73
|
+
app.modules = project.under(package)
|
|
74
|
+
app.models = [m for m in app.modules if m.dotted in (package + ".models", package + ".models.__init__") or m.dotted.startswith(package + ".models.")]
|
|
75
|
+
readme = os.path.join(directory, "README.md")
|
|
76
|
+
if os.path.isfile(readme):
|
|
77
|
+
app.readme = read(readme).strip()
|
|
78
|
+
return app
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def is_config(node) -> bool:
|
|
82
|
+
"""A class in `apps.py` deriving from AppConfig."""
|
|
83
|
+
return any(base.split(".")[-1] == "AppConfig" for base in bases(node))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def label_of(app: App) -> str:
|
|
87
|
+
"""The label Django would use for the application: `AppConfig.label` when
|
|
88
|
+
one is set, the package's last segment otherwise. It is half of every
|
|
89
|
+
default table name, which is why it is read rather than assumed."""
|
|
90
|
+
module = app.module("apps")
|
|
91
|
+
if module is not None:
|
|
92
|
+
for node in module.classes():
|
|
93
|
+
if not is_config(node):
|
|
94
|
+
continue
|
|
95
|
+
for stmt in node.body:
|
|
96
|
+
if isinstance(stmt, ast.Assign) and len(stmt.targets) == 1 and dotted(stmt.targets[0]) == "label":
|
|
97
|
+
value = stmt.value
|
|
98
|
+
if isinstance(value, ast.Constant) and isinstance(value.value, str):
|
|
99
|
+
return value.value
|
|
100
|
+
return app.label
|