@shortlink-org/portolan 0.2.4 → 0.4.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/README.md +174 -6
- package/catalog/enum_test.go +46 -0
- package/catalog/evidence_test.go +35 -0
- package/catalog/model.go +1213 -0
- package/catalog/roundtrip_test.go +210 -0
- package/catalog/via_test.go +38 -0
- package/cli/init.test.mjs +6 -1
- package/cli/portolan.mjs +14 -1
- package/cli/portolan.test.mjs +49 -0
- package/go.mod +21 -0
- package/go.sum +34 -0
- package/internal/gocall/README.md +19 -0
- package/internal/gocall/analyze.go +189 -0
- package/internal/gocall/analyze_test.go +107 -0
- package/internal/gohttp/analyze.go +2562 -0
- package/internal/gohttp/destination.go +373 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +96 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/index.go +629 -0
- package/internal/goscan/index_test.go +66 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +37 -0
- package/internal/goscan/tree.go +284 -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 +70 -0
- package/internal/wsdl/parse.go +949 -0
- package/internal/wsdl/parse_test.go +170 -0
- package/package.json +28 -15
- package/plugin/describe.go +156 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +141 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +404 -50
- package/plugins/cmd/portolan-http-clients/main.go +19 -0
- package/plugins/extract-argocd/options.schema.json +44 -0
- package/plugins/extract-celery/extract.py +0 -2
- package/plugins/extract-celery/extract_test.py +1 -1
- package/plugins/extract-celery/main.py +1 -0
- package/plugins/extract-csharp-ddd/README.md +213 -0
- package/plugins/extract-csharp-ddd/options.schema.json +55 -0
- package/plugins/extract-django/README.md +52 -19
- package/plugins/extract-django/choices.py +47 -0
- package/plugins/extract-django/domain.py +30 -18
- package/plugins/extract-django/extract.py +21 -7
- package/plugins/extract-django/extract_test.py +68 -2
- package/plugins/extract-django/lifecycle.py +4 -28
- package/plugins/extract-django/main.py +1 -0
- package/plugins/extract-django/operations.py +1 -1
- package/plugins/extract-django/routing_test.py +109 -1
- package/plugins/extract-django/rules.py +154 -0
- package/plugins/extract-django/rules_test.py +158 -0
- package/plugins/extract-django/store.py +1 -1
- package/plugins/extract-django/transport.py +101 -55
- package/plugins/extract-django/verbs.py +241 -0
- package/plugins/extract-go/README.md +47 -0
- package/plugins/extract-go-sqs/options.schema.json +21 -0
- package/plugins/extract-http-clients/describe.go +20 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +740 -0
- package/plugins/extract-http-clients/extract_test.go +1561 -0
- package/plugins/extract-http-clients/main.go +41 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.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-k8s/options.schema.json +32 -0
- package/plugins/extract-laravel/Cargo.lock +962 -0
- package/plugins/extract-laravel/Cargo.toml +20 -0
- package/plugins/extract-laravel/README.md +200 -0
- package/plugins/extract-laravel/options.schema.json +68 -0
- package/plugins/extract-laravel/rustfmt.toml +1 -0
- package/plugins/extract-php-ddd/Cargo.lock +972 -0
- package/plugins/extract-php-ddd/Cargo.toml +22 -0
- package/plugins/extract-php-ddd/README.md +141 -0
- package/plugins/extract-php-ddd/options.schema.json +50 -0
- package/plugins/extract-php-ddd/rustfmt.toml +1 -0
- package/plugins/extract-python-kafka/extract.py +0 -2
- package/plugins/extract-python-kafka/extract_test.py +1 -1
- package/plugins/extract-python-kafka/main.py +1 -0
- package/plugins/extract-terraform/options.schema.json +26 -0
- package/plugins/extract-ts/extract.test.ts +2 -2
- package/plugins/extract-ts/extract.ts +4 -5
- package/plugins/extract-ts/graphql.test.ts +1 -1
- package/plugins/extract-ts/main.ts +1 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/phpscan/Cargo.lock +953 -0
- package/plugins/phpscan/Cargo.toml +19 -0
- package/plugins/phpscan/rustfmt.toml +1 -0
- package/plugins/portolan-go.wasm +0 -0
- package/plugins/pyplugin/catalog.py +24 -1
- package/plugins/pyplugin/protocol.py +1 -5
- package/plugins/verify-otel/options.schema.json +12 -0
- package/portolan.json +132 -4
- package/rules/builtin.json +218 -0
- package/schema/portolan.schema.json +905 -4
- package/scripts/README.md +21 -13
- package/scripts/catalog-sources.mjs +6 -0
- package/scripts/delivery-presets.mjs +57 -14
- package/scripts/diff.mjs +5 -1
- package/scripts/django-aggregates.test.mjs +58 -0
- package/scripts/gen-likec4.mjs +150 -17
- package/scripts/gen-likec4.test.mjs +96 -0
- package/scripts/gen.mjs +148 -118
- package/scripts/gitops-example.test.mjs +108 -0
- package/scripts/go-discovery.test.mjs +30 -0
- package/scripts/history.mjs +186 -3
- package/scripts/history.test.mjs +1 -1
- package/scripts/host-plugins/fetch-argocd.mjs +338 -0
- package/scripts/host-plugins/fetch-argocd.options.json +49 -0
- package/scripts/host-plugins/fetch-argocd.test.mjs +274 -0
- package/scripts/host-plugins/fetch-bsr.mjs +1 -0
- package/scripts/host-plugins/fetch-csr.mjs +1 -0
- package/scripts/host-plugins/fetch-git.mjs +78 -21
- package/scripts/host-plugins/fetch-git.test.mjs +62 -8
- package/scripts/host-plugins/fetch-k8s.mjs +263 -0
- package/scripts/host-plugins/fetch-k8s.options.json +50 -0
- package/scripts/host-plugins/fetch-k8s.test.mjs +259 -0
- package/scripts/host-plugins/k8s-topology.mjs +183 -0
- package/scripts/json-format.mjs +192 -0
- package/scripts/json-format.test.mjs +97 -0
- package/scripts/local-api.mjs +287 -12
- package/scripts/local-api.test.mjs +150 -5
- package/scripts/local-discovery.mjs +94 -9
- package/scripts/manifest.mjs +14 -3
- package/scripts/manifest.test.mjs +24 -0
- package/scripts/output-diff.mjs +94 -0
- package/scripts/output-diff.test.mjs +36 -0
- package/scripts/package-smoke.mjs +62 -4
- package/scripts/plugin-host.mjs +45 -3
- package/scripts/plugin-host.test.mjs +9 -0
- package/scripts/plugin-wasm-worker.mjs +4 -1
- package/scripts/plugins-fresh.mjs +97 -0
- package/scripts/plugins-fresh.test.mjs +64 -0
- package/scripts/provenance.mjs +72 -0
- package/scripts/provenance.test.mjs +149 -0
- package/scripts/run-builtin.mjs +62 -7
- package/scripts/schema.mjs +167 -19
- package/scripts/trace-trials.mjs +176 -0
- package/scripts/trace-trials.test.mjs +142 -0
- package/scripts/warning-policy.mjs +167 -0
- package/scripts/warning-policy.test.mjs +93 -0
- package/src/app/Breadcrumbs.test.ts +3 -0
- package/src/app/Breadcrumbs.tsx +3 -0
- package/src/app/CatalogApp.tsx +2 -0
- package/src/app/Sidebar.tsx +3 -3
- package/src/app/SidebarFooter.tsx +20 -4
- package/src/catalog-docs.test.ts +64 -0
- package/src/catalog-docs.ts +35 -0
- package/src/catalog-error.test.ts +15 -0
- package/src/catalog-index.ts +25 -0
- package/src/catalog-model.ts +293 -5
- package/src/catalog-validation.ts +113 -2
- package/src/catalog.test.ts +40 -1
- package/src/chat/Starter.tsx +5 -11
- package/src/chat/tools.test.ts +27 -0
- package/src/chat/tools.ts +5 -9
- package/src/components/CatalogStamp.tsx +10 -8
- package/src/components/ChannelRows.test.tsx +76 -1
- package/src/components/ChannelRows.tsx +223 -60
- package/src/components/DeploymentRows.tsx +131 -0
- package/src/components/DocsLinks.test.tsx +27 -0
- package/src/components/DocsLinks.tsx +56 -0
- package/src/components/FieldTree.tsx +5 -2
- package/src/components/HTTPDestinationEvidence.test.tsx +23 -0
- package/src/components/HTTPDestinationEvidence.tsx +31 -0
- package/src/components/Integrations.tsx +1 -1
- package/src/components/MachineDocs.tsx +6 -5
- package/src/components/MethodRows.tsx +9 -2
- package/src/components/PluginIcon.tsx +77 -0
- package/src/components/ProblemRow.tsx +173 -153
- package/src/components/RelationEvidence.test.tsx +14 -0
- package/src/components/RelationEvidence.tsx +53 -0
- package/src/components/RuleMarks.tsx +22 -0
- package/src/components/ShapeRows.tsx +24 -20
- package/src/data.ts +46 -7
- package/src/enrich.test.ts +459 -4
- package/src/enrich.ts +308 -7
- package/src/er/ErCanvas.tsx +218 -12
- package/src/er/GroupNode.tsx +57 -0
- package/src/er/StoreHeader.tsx +1 -0
- package/src/er/layout.test.ts +85 -2
- package/src/er/layout.ts +140 -5
- package/src/er/spec.test.ts +17 -0
- package/src/er/spec.ts +23 -10
- package/src/flow/Recordings.test.tsx +52 -0
- package/src/flow/Recordings.tsx +236 -0
- package/src/flow/StepDetail.tsx +59 -0
- package/src/flow/TraceTrial.tsx +419 -0
- package/src/flow/evidence.test.ts +16 -0
- package/src/flow/evidence.ts +34 -0
- package/src/flow/examples.test.ts +33 -0
- package/src/flow/examples.ts +37 -0
- package/src/flow/outline.test.ts +28 -0
- package/src/flow/outline.ts +7 -2
- package/src/flow/trace-trial-resume.test.ts +42 -0
- package/src/flow/trace-trial-resume.ts +74 -0
- package/src/graph/elk.ts +78 -0
- package/src/index.css +68 -0
- package/src/landing/DraggableReveal.tsx +3 -2
- package/src/landing/EvidencePipeline.tsx +105 -0
- package/src/landing/LandingPage.tsx +17 -70
- package/src/landing/ProductTour.tsx +6 -6
- package/src/lib/all-problems.ts +27 -17
- package/src/lib/catalog-diff.ts +1 -1
- package/src/lib/centrality.test.ts +251 -0
- package/src/lib/centrality.ts +232 -0
- package/src/lib/confluence.test.ts +37 -0
- package/src/lib/confluence.ts +41 -0
- package/src/lib/context-color.ts +1 -1
- package/src/lib/deployment-drift.ts +22 -0
- package/src/lib/derive.ts +43 -69
- package/src/lib/django-aggregates.d.mts +9 -0
- package/src/lib/django-aggregates.mjs +36 -0
- package/src/lib/django-aggregates.test.ts +29 -0
- package/src/lib/django-aggregates.ts +5 -0
- package/src/lib/environments.test.ts +83 -0
- package/src/lib/environments.ts +57 -0
- package/src/lib/integration-url.test.ts +30 -0
- package/src/lib/integration-url.ts +63 -0
- package/src/lib/kafka-ui.ts +3 -45
- package/src/lib/local-api.ts +116 -4
- package/src/lib/notion.ts +13 -0
- package/src/lib/plugin-index.json +2765 -0
- package/src/lib/plugins.test.ts +68 -0
- package/src/lib/plugins.ts +264 -0
- package/src/lib/problem-flows.test.ts +61 -0
- package/src/lib/problem-flows.ts +78 -0
- package/src/lib/problem-rules-cel.d.mts +43 -0
- package/src/lib/problem-rules-cel.mjs +407 -0
- package/src/lib/problem-rules.test.ts +287 -0
- package/src/lib/problem-rules.ts +271 -0
- package/src/lib/problem-subjects.ts +737 -0
- package/src/lib/rule-entries.ts +39 -0
- package/src/lib/{data-problems.test.ts → rules-data.test.ts} +18 -16
- package/src/lib/rules-deploy.test.ts +133 -0
- package/src/lib/{problems.test.ts → rules-edges.test.ts} +8 -3
- package/src/lib/{proto-problems.test.ts → rules-proto.test.ts} +9 -25
- package/src/lib/{wire-problems.test.ts → rules-wire.test.ts} +13 -11
- package/src/lib/rules.test.ts +51 -0
- package/src/lib/rules.ts +86 -0
- package/src/lib/setup-info.test.ts +17 -0
- package/src/lib/setup-info.ts +58 -0
- package/src/lib/shape.test.ts +32 -0
- package/src/lib/shape.ts +30 -6
- package/src/lib/tech.ts +16 -0
- package/src/lib/trace-project.test.ts +34 -0
- package/src/lib/trace-project.ts +50 -0
- package/src/lib/use-problems.ts +23 -0
- package/src/lib/warnings.test.ts +63 -0
- package/src/lib/warnings.ts +260 -0
- package/src/likec4/ids.test.ts +6 -2
- package/src/likec4/ids.ts +43 -0
- package/src/main.tsx +23 -0
- package/src/map/ContextMapGraph.tsx +76 -32
- package/src/merge-deployments.test.ts +127 -0
- package/src/merge.test.ts +82 -0
- package/src/merge.ts +189 -18
- package/src/pages/AggregatePage.tsx +65 -14
- package/src/pages/ContextMap.tsx +45 -3
- package/src/pages/ContextPage.tsx +8 -5
- package/src/pages/EventPage.tsx +15 -5
- package/src/pages/FlowDetail.tsx +23 -2
- package/src/pages/GraphPage.tsx +40 -3
- package/src/pages/Overview.tsx +152 -12
- package/src/pages/PluginIndex.tsx +190 -0
- package/src/pages/Problems.tsx +396 -128
- package/src/pages/ServicePage.tsx +62 -5
- package/src/pages/Settings.tsx +217 -43
- package/src/pages/settings/AboutSettings.tsx +8 -1
- package/src/pages/settings/DjangoAggregateChoices.tsx +79 -0
- package/src/pages/settings/IntegrationsSettings.tsx +63 -17
- package/src/pages/settings/RecordingSettings.tsx +138 -0
- package/src/pages/settings/RulesSettings.tsx +825 -0
- package/src/routes.test.ts +9 -0
- package/src/routes.ts +22 -1
- package/src/selection/DetailPanel.tsx +15 -0
- package/src/virtual-provenance.d.ts +11 -0
- package/vite.config.ts +5 -0
- package/scripts/vendor-lock.mjs +0 -58
- package/scripts/vendor-lock.test.mjs +0 -69
- package/src/lib/data-problems.ts +0 -314
- package/src/lib/proto-problems.ts +0 -237
- package/src/lib/wire-problems.ts +0 -342
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "phpscan"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
description = "A PHP tree as syntax, for every PHP extractor: files parsed once with Mago, classes and the call chains their code makes, names resolved by namespace and use line."
|
|
6
|
+
publish = false
|
|
7
|
+
|
|
8
|
+
[lib]
|
|
9
|
+
path = "src/lib.rs"
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
# Mago is the PHP toolchain written in Rust; its syntax crate is the one
|
|
13
|
+
# maintained PHP parser in this language that keeps up with the language, and
|
|
14
|
+
# the arena it parses into is dropped once a file has been read into the
|
|
15
|
+
# extractor's own shapes.
|
|
16
|
+
mago-syntax = "1.48.1"
|
|
17
|
+
mago-database = "1.48.1"
|
|
18
|
+
mago-allocator = "1.48.1"
|
|
19
|
+
mago-span = "1.48.1"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
max_width = 160
|
package/plugins/portolan-go.wasm
CHANGED
|
Binary file
|
|
@@ -13,10 +13,33 @@ DECLARED = "declared"
|
|
|
13
13
|
UNRESOLVED = "unresolved"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def field(
|
|
16
|
+
def field(
|
|
17
|
+
name: str,
|
|
18
|
+
type_: str,
|
|
19
|
+
doc: str = "",
|
|
20
|
+
ref: str = "",
|
|
21
|
+
required: bool = False,
|
|
22
|
+
rules: Optional[List[Dict[str, str]]] = None,
|
|
23
|
+
) -> Dict[str, Any]:
|
|
24
|
+
"""A field. `required` is written only when true and `rules` only when
|
|
25
|
+
there are any: a source that says nothing about a value leaves nothing."""
|
|
17
26
|
out = {"name": name, "type": type_, "doc": doc}
|
|
18
27
|
if ref:
|
|
19
28
|
out["ref"] = ref
|
|
29
|
+
if required:
|
|
30
|
+
out["required"] = True
|
|
31
|
+
if rules:
|
|
32
|
+
out["rules"] = rules
|
|
33
|
+
return out
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def rule(name: str, value: str = "") -> Dict[str, str]:
|
|
37
|
+
"""One rule on a value, in the catalog's vocabulary (portolan.0015):
|
|
38
|
+
`min_len`, `pattern`, `in`, `format`, `gte`... The value is text, as the
|
|
39
|
+
source wrote it; a bare flag such as `unique` has none."""
|
|
40
|
+
out = {"name": name}
|
|
41
|
+
if value:
|
|
42
|
+
out["value"] = value
|
|
20
43
|
return out
|
|
21
44
|
|
|
22
45
|
|
|
@@ -17,12 +17,10 @@ from typing import Any, Dict, List
|
|
|
17
17
|
|
|
18
18
|
@dataclass
|
|
19
19
|
class Input:
|
|
20
|
-
"""Where the source is
|
|
20
|
+
"""Where the source is."""
|
|
21
21
|
|
|
22
22
|
root: str = ""
|
|
23
23
|
output: str = ""
|
|
24
|
-
commit: str = ""
|
|
25
|
-
generated_at: str = ""
|
|
26
24
|
|
|
27
25
|
@staticmethod
|
|
28
26
|
def of(raw: Any) -> "Input":
|
|
@@ -30,8 +28,6 @@ class Input:
|
|
|
30
28
|
return Input(
|
|
31
29
|
root=raw.get("root", ""),
|
|
32
30
|
output=raw.get("output", ""),
|
|
33
|
-
commit=raw.get("commit", ""),
|
|
34
|
-
generated_at=raw.get("generatedAt", ""),
|
|
35
31
|
)
|
|
36
32
|
|
|
37
33
|
|
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
"description": "Which catalog event a wire name belongs to, for an estate where the event does not declare its wire name and the last segment of the event.name attribute is not the event's name in the model.",
|
|
23
23
|
"examples": [{ "auth.PasswordChanged": "auth.auth.PasswordChanged" }]
|
|
24
24
|
},
|
|
25
|
+
"routes": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": { "type": "string" },
|
|
28
|
+
"description": "Which operation a route a server span answers on belongs to - the verb and the http.route, to the operation's name in the service's interface - for an estate where the two do not line up: a prefix a gateway adds, a path the document spells another way. Without a line here a route is matched by verb and path shape.",
|
|
29
|
+
"examples": [{ "POST /api/v1/sessions": "login" }]
|
|
30
|
+
},
|
|
31
|
+
"examples": {
|
|
32
|
+
"type": "integer",
|
|
33
|
+
"minimum": 0,
|
|
34
|
+
"description": "How many recordings a flow keeps as examples of itself running: the ones that show the most of it, earliest first. Five when unset; zero keeps none.",
|
|
35
|
+
"examples": [3]
|
|
36
|
+
},
|
|
25
37
|
"out": {
|
|
26
38
|
"type": "string",
|
|
27
39
|
"description": "Name of the fragment file, inside the step's output directory."
|
package/portolan.json
CHANGED
|
@@ -19,10 +19,32 @@
|
|
|
19
19
|
"examples/*/*/portolan/*.json",
|
|
20
20
|
"examples/*/*/*/portolan/*.json",
|
|
21
21
|
"vendor/repos/*/*/git.repo.json",
|
|
22
|
-
"vendor/repos/**/portolan/*.json"
|
|
22
|
+
"vendor/repos/**/portolan/*.json",
|
|
23
|
+
"examples/argocd/argocd.apps.json"
|
|
23
24
|
],
|
|
24
25
|
"contexts": ["auth", "shop", "delivery", "payments", "storefront"],
|
|
25
26
|
"projects": ["auth", "shop-cart", "shop-billing", "shop-pricing", "delivery-core", "payments-ledger", "shop-oms"]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "bagisto",
|
|
30
|
+
"title": "Bagisto",
|
|
31
|
+
"sources": ["vendor/repos/bagisto/bagisto/git.repo.json", "vendor/repos/bagisto/bagisto/catalog/*.json"],
|
|
32
|
+
"contexts": ["commerce"],
|
|
33
|
+
"projects": []
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "codely",
|
|
37
|
+
"title": "CodelyTV php-ddd-example",
|
|
38
|
+
"sources": ["vendor/repos/CodelyTV/php-ddd-example/git.repo.json", "vendor/repos/CodelyTV/php-ddd-example/catalog/*.json"],
|
|
39
|
+
"contexts": ["mooc", "backoffice", "analytics", "retention"],
|
|
40
|
+
"projects": []
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "modular-monolith",
|
|
44
|
+
"title": "Modular Monolith with DDD",
|
|
45
|
+
"sources": ["vendor/repos/kgrzybek/modular-monolith-with-ddd/git.repo.json", "vendor/repos/kgrzybek/modular-monolith-with-ddd/catalog/*.json"],
|
|
46
|
+
"contexts": ["administration", "meetings", "payments", "registrations", "user-access"],
|
|
47
|
+
"projects": []
|
|
26
48
|
}
|
|
27
49
|
],
|
|
28
50
|
"projects": [
|
|
@@ -90,7 +112,8 @@
|
|
|
90
112
|
"examples/*/*/portolan/*.json",
|
|
91
113
|
"examples/*/*/*/portolan/*.json",
|
|
92
114
|
"vendor/repos/*/*/git.repo.json",
|
|
93
|
-
"vendor/repos/**/portolan/*.json"
|
|
115
|
+
"vendor/repos/**/portolan/*.json",
|
|
116
|
+
"examples/argocd/argocd.apps.json"
|
|
94
117
|
],
|
|
95
118
|
"plugins": [
|
|
96
119
|
{
|
|
@@ -125,6 +148,27 @@
|
|
|
125
148
|
"args": ["run", "--quiet", "--manifest-path", "plugins/extract-rust/Cargo.toml"]
|
|
126
149
|
}
|
|
127
150
|
},
|
|
151
|
+
{
|
|
152
|
+
"name": "laravel-domain",
|
|
153
|
+
"process": {
|
|
154
|
+
"command": "cargo",
|
|
155
|
+
"args": ["run", "--quiet", "--manifest-path", "plugins/extract-laravel/Cargo.toml"]
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "php-ddd",
|
|
160
|
+
"process": {
|
|
161
|
+
"command": "cargo",
|
|
162
|
+
"args": ["run", "--quiet", "--manifest-path", "plugins/extract-php-ddd/Cargo.toml"]
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"name": "csharp-ddd",
|
|
167
|
+
"process": {
|
|
168
|
+
"command": "dotnet",
|
|
169
|
+
"args": ["plugins/extract-csharp-ddd/bin/portolan-extract-csharp-ddd.dll"]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
128
172
|
{
|
|
129
173
|
"name": "java-domain",
|
|
130
174
|
"process": {
|
|
@@ -167,8 +211,9 @@
|
|
|
167
211
|
},
|
|
168
212
|
{
|
|
169
213
|
"name": "http-clients",
|
|
170
|
-
"
|
|
171
|
-
"
|
|
214
|
+
"process": {
|
|
215
|
+
"command": "go",
|
|
216
|
+
"args": ["run", "-mod=mod", "./plugins/cmd/portolan-http-clients"]
|
|
172
217
|
}
|
|
173
218
|
},
|
|
174
219
|
{
|
|
@@ -195,6 +240,30 @@
|
|
|
195
240
|
"url": "file://plugins/portolan-go.wasm"
|
|
196
241
|
}
|
|
197
242
|
},
|
|
243
|
+
{
|
|
244
|
+
"name": "go-sqs",
|
|
245
|
+
"wasm": {
|
|
246
|
+
"url": "file://plugins/portolan-go.wasm"
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "terraform",
|
|
251
|
+
"wasm": {
|
|
252
|
+
"url": "file://plugins/portolan-go.wasm"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "k8s",
|
|
257
|
+
"wasm": {
|
|
258
|
+
"url": "file://plugins/portolan-go.wasm"
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "argocd-gitops",
|
|
263
|
+
"wasm": {
|
|
264
|
+
"url": "file://plugins/portolan-go.wasm"
|
|
265
|
+
}
|
|
266
|
+
},
|
|
198
267
|
{
|
|
199
268
|
"name": "asyncapi",
|
|
200
269
|
"wasm": {
|
|
@@ -273,6 +342,14 @@
|
|
|
273
342
|
"name": "csr",
|
|
274
343
|
"host": "fetch-csr"
|
|
275
344
|
},
|
|
345
|
+
{
|
|
346
|
+
"name": "k8s-cluster",
|
|
347
|
+
"host": "fetch-k8s"
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"name": "argocd",
|
|
351
|
+
"host": "fetch-argocd"
|
|
352
|
+
},
|
|
276
353
|
{
|
|
277
354
|
"name": "csr-schemas",
|
|
278
355
|
"wasm": {
|
|
@@ -444,6 +521,17 @@
|
|
|
444
521
|
"out": "commands.json"
|
|
445
522
|
}
|
|
446
523
|
},
|
|
524
|
+
{
|
|
525
|
+
"plugin": "k8s",
|
|
526
|
+
"in": "examples/shop/cart",
|
|
527
|
+
"out": "examples/shop/cart/portolan",
|
|
528
|
+
"options": {
|
|
529
|
+
"context": "shop",
|
|
530
|
+
"service": "cart",
|
|
531
|
+
"paths": ["deploy/k8s"],
|
|
532
|
+
"out": "k8s.json"
|
|
533
|
+
}
|
|
534
|
+
},
|
|
447
535
|
{
|
|
448
536
|
"plugin": "ts-domain",
|
|
449
537
|
"in": "examples/shop/cart",
|
|
@@ -615,6 +703,17 @@
|
|
|
615
703
|
"out": "commands.json"
|
|
616
704
|
}
|
|
617
705
|
},
|
|
706
|
+
{
|
|
707
|
+
"plugin": "k8s",
|
|
708
|
+
"in": "examples/shop/pricing",
|
|
709
|
+
"out": "examples/shop/pricing/portolan",
|
|
710
|
+
"options": {
|
|
711
|
+
"context": "shop",
|
|
712
|
+
"service": "pricing",
|
|
713
|
+
"paths": ["deploy/k8s"],
|
|
714
|
+
"out": "k8s.json"
|
|
715
|
+
}
|
|
716
|
+
},
|
|
618
717
|
{
|
|
619
718
|
"plugin": "go-nats",
|
|
620
719
|
"in": "examples/shop/pricing",
|
|
@@ -813,6 +912,26 @@
|
|
|
813
912
|
]
|
|
814
913
|
}
|
|
815
914
|
},
|
|
915
|
+
{
|
|
916
|
+
"plugin": "argocd-gitops",
|
|
917
|
+
"in": "examples/gitops",
|
|
918
|
+
"out": "examples/gitops/portolan",
|
|
919
|
+
"options": {
|
|
920
|
+
"repo": "github.com/shortlink-org/portolan",
|
|
921
|
+
"paths": ["bootstrap", "appsets"],
|
|
922
|
+
"out": "argocd.json"
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
"plugin": "argocd",
|
|
927
|
+
"in": "examples",
|
|
928
|
+
"out": "examples/argocd",
|
|
929
|
+
"options": {
|
|
930
|
+
"server": "argocd.example.com",
|
|
931
|
+
"projects": ["shop", "platform"],
|
|
932
|
+
"cache": "examples/argocd"
|
|
933
|
+
}
|
|
934
|
+
},
|
|
816
935
|
{
|
|
817
936
|
"plugin": "proto",
|
|
818
937
|
"in": "examples/shop/oms",
|
|
@@ -956,6 +1075,15 @@
|
|
|
956
1075
|
"context": "storefront",
|
|
957
1076
|
"out": "glossary.json"
|
|
958
1077
|
}
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
"plugin": "k8s-cluster",
|
|
1081
|
+
"in": ".",
|
|
1082
|
+
"out": "data",
|
|
1083
|
+
"options": {
|
|
1084
|
+
"cache": "data",
|
|
1085
|
+
"out": "k8s-cluster.json"
|
|
1086
|
+
}
|
|
959
1087
|
}
|
|
960
1088
|
],
|
|
961
1089
|
"verify": [
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "rpc",
|
|
4
|
+
"over": "call",
|
|
5
|
+
"severity": "error",
|
|
6
|
+
"title": "Call resolves to nothing",
|
|
7
|
+
"note": "the provider of this call is not in the catalog",
|
|
8
|
+
"description": "A service calls a method, by interface and name, and no service in the catalog declares that interface. The chart draws an arrow into open water.",
|
|
9
|
+
"action": "Add the provider's contract to the catalog, or map the peer under externals, then regenerate.",
|
|
10
|
+
"when": "!call.resolved",
|
|
11
|
+
"message": "call.note",
|
|
12
|
+
"peer": "call.peer"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "consumer",
|
|
16
|
+
"over": "consumer",
|
|
17
|
+
"severity": "error",
|
|
18
|
+
"title": "Consumer is not in the catalog",
|
|
19
|
+
"note": "this consumer of the event is not in the catalog",
|
|
20
|
+
"description": "An event names a consuming service that no source in the catalog declares. Either the consumer's repository has not been read, or the name is spelled differently on the two sides.",
|
|
21
|
+
"action": "Point an extractor at the consuming service, or correct the consumer's name where the event declares it.",
|
|
22
|
+
"when": "!consumer.resolved",
|
|
23
|
+
"message": "consumer.note",
|
|
24
|
+
"peer": "consumer.consumer"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "cross-service-fk",
|
|
28
|
+
"over": "column",
|
|
29
|
+
"severity": "error",
|
|
30
|
+
"title": "Foreign key across a service boundary",
|
|
31
|
+
"note": "foreign key across a service boundary",
|
|
32
|
+
"description": "A column references a table another service owns. The database enforces a relation the services' contracts do not know about, and one migration can break the other service.",
|
|
33
|
+
"action": "Carry the reference as a plain id and resolve it through the owning service's contract, or move the two tables under one owner.",
|
|
34
|
+
"when": "column.fkOwner != '' && column.fkOwner != column.owner",
|
|
35
|
+
"message": "column.owner + ' holds a foreign key into a table owned by ' + column.fkOwner + '; neither service can migrate that table alone.'",
|
|
36
|
+
"peer": "column.fk"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "cross-service-lineage",
|
|
40
|
+
"over": "column",
|
|
41
|
+
"severity": "warning",
|
|
42
|
+
"title": "Value copied from another service's schema",
|
|
43
|
+
"note": "a value copied from another service's schema",
|
|
44
|
+
"description": "A column is computed from a column in a store another service owns. The copy follows the source's schema without a contract saying so, and nothing on the other side records that the copy exists.",
|
|
45
|
+
"action": "Feed the projection from the owning service's events, or document the read as an accepted coupling.",
|
|
46
|
+
"when": "size(column.foreignFrom) > 0",
|
|
47
|
+
"message": "column.owner + ' copies this value from ' + column.foreignFrom.join(', ') + ', which ' + column.foreignOwners.join(', ') + ' owns; nothing on that side records that the copy exists.'",
|
|
48
|
+
"peer": "column.foreignFrom[0]"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "cross-service-view",
|
|
52
|
+
"over": "table",
|
|
53
|
+
"severity": "warning",
|
|
54
|
+
"title": "View defined over another service's relation",
|
|
55
|
+
"note": "a view over another service's table",
|
|
56
|
+
"description": "A view reads a table or view in a store another service owns. A rename over there breaks this view with no error until it is read.",
|
|
57
|
+
"action": "Build the view over a local projection fed by the owner's events, or document the read as an accepted coupling.",
|
|
58
|
+
"when": "table.kind == 'view' && size(table.foreignReads) > 0",
|
|
59
|
+
"message": "table.id + ' is defined over ' + table.foreignReads.join(', ') + ', which another service owns; a rename over there breaks this view with no error until it is read.'",
|
|
60
|
+
"peer": "table.foreignReads[0]"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "shared-store",
|
|
64
|
+
"over": "table",
|
|
65
|
+
"severity": "error",
|
|
66
|
+
"title": "Second writer on a database",
|
|
67
|
+
"note": "a second service writes this database",
|
|
68
|
+
"description": "A table in one service's store holds an aggregate another service writes. Two services now share one schema, and neither can change it alone. A projection is a copy maintained from the owner's events and is not this.",
|
|
69
|
+
"action": "Move the write behind the owner's API or events, or give the table to the service that writes it.",
|
|
70
|
+
"when": "table.kind == 'table' && table.role != 'projection' && table.aggregateOwner != '' && table.aggregateOwner != table.owner",
|
|
71
|
+
"message": "table.store + ' is owned by ' + table.owner + ', but this table holds ' + table.aggregate + ', which ' + table.aggregateOwner + ' writes.'",
|
|
72
|
+
"peer": "table.aggregateOwner"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "persistence-drift",
|
|
76
|
+
"over": "table",
|
|
77
|
+
"severity": "warning",
|
|
78
|
+
"title": "Table no longer holds the aggregate it claims",
|
|
79
|
+
"note": "this table no longer carries the aggregate it claims",
|
|
80
|
+
"description": "A table says it persists an aggregate, and no column of it maps to a field the aggregate declares any more. The table was relabelled and the columns were not, or the aggregate moved on without it.",
|
|
81
|
+
"action": "Point the table at the aggregate that replaced it, map its columns again, or drop the persists claim.",
|
|
82
|
+
"when": "table.kind == 'table' && table.role != 'projection' && table.role != 'outbox' && table.aggregate != '' && table.aggregateFields > 0 && table.mappedColumns == 0",
|
|
83
|
+
"message": "table.claimedColumns == 0 ? 'no column of ' + table.name + ' maps to a field of ' + table.aggregate : string(table.claimedColumns) + ' column(s) of ' + table.name + ' map to fields ' + table.aggregate + ' no longer declares'",
|
|
84
|
+
"peer": "table.aggregate"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "column-type",
|
|
88
|
+
"over": "column",
|
|
89
|
+
"severity": "warning",
|
|
90
|
+
"title": "Column type and domain type disagree",
|
|
91
|
+
"note": "column type and domain type disagree",
|
|
92
|
+
"description": "A column is mapped to a domain field, and the database type is not one the field's type is stored as. One side has changed without the other.",
|
|
93
|
+
"action": "Align the migration with the field's type, or correct the mapping if the column holds something else.",
|
|
94
|
+
"when": "column.kind == 'table' && column.maps != '' && column.fieldType != '' && !column.typeMatches",
|
|
95
|
+
"message": "'column is ' + column.type + ', ' + column.maps + ' is ' + column.fieldType",
|
|
96
|
+
"peer": "column.maps"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "outbox-payload",
|
|
100
|
+
"over": "table",
|
|
101
|
+
"severity": "warning",
|
|
102
|
+
"title": "Outbox without a payload column",
|
|
103
|
+
"note": "an outbox with no payload column",
|
|
104
|
+
"description": "A table plays the outbox role and has no json column that could carry a message body. The whole point of the pattern is that the event body is committed with the state change, so this is either not an outbox or not finished.",
|
|
105
|
+
"action": "Add a payload column to the outbox table, or clear its role if it is not an outbox.",
|
|
106
|
+
"when": "table.role == 'outbox' && !table.hasPayload",
|
|
107
|
+
"message": "'an outbox carries the event body in the same transaction as the state change; this table declares no json column to hold one'",
|
|
108
|
+
"peer": "table.store"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "proto-missing",
|
|
112
|
+
"over": "call",
|
|
113
|
+
"severity": "error",
|
|
114
|
+
"title": "Provider answers on no such method",
|
|
115
|
+
"note": "the provider is in the catalog but answers on no such method",
|
|
116
|
+
"description": "A call names a service the catalog knows and a method that service's interfaces do not declare. The caller's vendored copy is ahead of, or behind, the provider, and the call will fail.",
|
|
117
|
+
"action": "Regenerate the caller's vendored proto from the provider's module, or add the method on the provider.",
|
|
118
|
+
"when": "call.resolved && call.peerKnown && !call.methodDeclared",
|
|
119
|
+
"message": "call.note",
|
|
120
|
+
"peer": "call.peer"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "proto-drift",
|
|
124
|
+
"over": "copy",
|
|
125
|
+
"severity": "warning",
|
|
126
|
+
"title": "Vendored proto and provider schema disagree",
|
|
127
|
+
"note": "the vendored proto and provider schema disagree",
|
|
128
|
+
"description": "The copy of an interface a caller compiled against differs from the interface the provider publishes: a message, a field or an enum value is not the same on both sides.",
|
|
129
|
+
"action": "Pin the caller to the provider's published module version and regenerate the copy.",
|
|
130
|
+
"when": "copy.provider != '' && size(copy.differences) > 0",
|
|
131
|
+
"message": "'Vendored copy differs from ' + copy.provider + ': ' + copy.differences.join('; ') + '.'",
|
|
132
|
+
"peer": "copy.provider"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "shared-channel",
|
|
136
|
+
"over": "channel",
|
|
137
|
+
"severity": "error",
|
|
138
|
+
"title": "Second publisher on a channel",
|
|
139
|
+
"note": "a second service publishes on this channel",
|
|
140
|
+
"description": "Two services publish on one channel, by an event that names it or a document that declares a send on it. A subscriber dispatches on the event's name and cannot tell whose message arrived, until one it cannot parse does.",
|
|
141
|
+
"action": "Give each publisher its own channel, named after the aggregate whose events travel on it.",
|
|
142
|
+
"when": "channel.publishes && size(channel.otherPublishers) > 0",
|
|
143
|
+
"message": "channel.service + ' publishes on ' + channel.address + ', and so does ' + channel.otherPublishers.join(', ') + ': ' + channel.otherClaims.join('; ') + '. A subscriber reads both without being told.'",
|
|
144
|
+
"peer": "channel.otherPublishers[0]"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "channel-undeclared",
|
|
148
|
+
"over": "event",
|
|
149
|
+
"severity": "warning",
|
|
150
|
+
"title": "Event leaves on an undeclared channel",
|
|
151
|
+
"note": "this event goes out on a channel the service does not declare",
|
|
152
|
+
"description": "The code publishes an event on a channel the service's AsyncAPI document does not list among its sends. The document is behind the code. A service whose document declares no sends says nothing here.",
|
|
153
|
+
"action": "Declare the channel and its messages in the service's AsyncAPI document.",
|
|
154
|
+
"when": "event.channel != '' && size(event.declaredChannels) > 0 && !(event.channel in event.declaredChannels)",
|
|
155
|
+
"message": "event.name + ' goes out on ' + event.channel + ', which ' + event.service + ' does not declare it sends on. A subscriber reading the document does not know this message exists.'",
|
|
156
|
+
"peer": "event.channel"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "channel-unpublished",
|
|
160
|
+
"over": "channel",
|
|
161
|
+
"severity": "warning",
|
|
162
|
+
"title": "Declared channel nothing publishes on",
|
|
163
|
+
"note": "a declared channel no event of this service names",
|
|
164
|
+
"description": "The service's document declares it sends domain events on a channel, and no event of the service names that channel as its wire. The code is behind the document, or the channel is dead.",
|
|
165
|
+
"action": "Remove the channel from the document, or wire the event that should leave on it.",
|
|
166
|
+
"when": "channel.declared && channel.kind == 'event' && size(channel.sends) > 0 && size(channel.events) == 0",
|
|
167
|
+
"message": "channel.service + ' declares it sends on ' + channel.address + ', and no event of it names that channel: ' + channel.sends.join(', ') + '.'",
|
|
168
|
+
"peer": "channel.address"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "message-encoding",
|
|
172
|
+
"over": "subscription",
|
|
173
|
+
"severity": "error",
|
|
174
|
+
"title": "Publisher and subscriber encode differently",
|
|
175
|
+
"note": "publisher and subscriber use different payload encodings",
|
|
176
|
+
"description": "Both sides of a message have named a payload encoding, and the two names differ. The subscriber will not read what the publisher writes.",
|
|
177
|
+
"action": "Agree one encoding in both AsyncAPI documents.",
|
|
178
|
+
"when": "subscription.encoding != '' && size(subscription.mismatched) > 0",
|
|
179
|
+
"message": "subscription.service + ' expects ' + subscription.name + ' on ' + subscription.channel + ' as ' + subscription.encoding + ', but ' + subscription.mismatched[0] + ' sends it as ' + subscription.mismatchedEncodings[0] + '.'",
|
|
180
|
+
"peer": "subscription.mismatched[0]"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "subscription-unresolved",
|
|
184
|
+
"over": "subscription",
|
|
185
|
+
"severity": "warning",
|
|
186
|
+
"title": "Subscription nothing publishes",
|
|
187
|
+
"note": "nothing in the catalog publishes what this service listens for",
|
|
188
|
+
"description": "A service declares it receives a message, and no event of the estate goes by that name on the wire and no document declares a send of it. The publisher is outside the estate, or the name has drifted.",
|
|
189
|
+
"action": "Read the publisher's repository into the catalog, or remove the subscription.",
|
|
190
|
+
"when": "!subscription.published",
|
|
191
|
+
"message": "subscription.service + ' listens on ' + subscription.channel + ' for ' + subscription.name + ', and nothing in the catalog publishes it. Either the publisher is outside the estate, or the name has drifted.'",
|
|
192
|
+
"peer": "subscription.name"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"id": "deployment-unclaimed",
|
|
196
|
+
"over": "deployment",
|
|
197
|
+
"severity": "warning",
|
|
198
|
+
"title": "Deployed from nowhere the catalog knows",
|
|
199
|
+
"note": "the deployer runs this from a repository and directory no service in the catalog lives at",
|
|
200
|
+
"description": "The deployer runs an Application from a repository and path, or with labels, that match no service. It is a service nobody has read yet, a path spelled differently, or infrastructure that is not a service at all.",
|
|
201
|
+
"action": "Read the service into the catalog, align its path with where its manifests live, or switch this rule off for infrastructure with a reason.",
|
|
202
|
+
"when": "!deployment.claimed",
|
|
203
|
+
"message": "deployment.name + ' runs in ' + deployment.runsIn + (deployment.namespace != '' ? ', namespace ' + deployment.namespace : '') + '; ' + (deployment.labelled != '' ? 'its labels name ' + deployment.labelled + ', which is no service of the estate' : 'if it is a service of the estate, its `path` should be a directory the Application\\'s path is under, or the Application should carry app.kubernetes.io/part-of and app.kubernetes.io/name') + '. ' + deployment.url",
|
|
204
|
+
"peer": "deployment.from"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "deployment-drift",
|
|
208
|
+
"over": "deployment",
|
|
209
|
+
"severity": "warning",
|
|
210
|
+
"title": "GitOps tree and deployer disagree",
|
|
211
|
+
"note": "the GitOps tree and the deployer disagree about this Application",
|
|
212
|
+
"description": "What the GitOps tree says should run and what the deployer reports is running differ on a project, cluster, namespace, path, revision or image.",
|
|
213
|
+
"action": "Sync the Application, or commit to the tree what was changed on the cluster.",
|
|
214
|
+
"when": "deployment.drifted",
|
|
215
|
+
"message": "deployment.drift + '. ' + deployment.url",
|
|
216
|
+
"peer": "deployment.environment"
|
|
217
|
+
}
|
|
218
|
+
]
|