| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186 |
- #!/usr/bin/env python3
- """Generate the current Flask route inventory as an OpenAPI 3.1 contract."""
- from __future__ import annotations
- import argparse
- import ast
- import json
- import re
- from collections import defaultdict
- from pathlib import Path
- ROOT = Path(__file__).resolve().parents[1]
- OUTPUT = ROOT / "docs" / "architecture" / "OPENAPI.yaml"
- PREFIXES = {
- "meta_data": "/api/meta",
- "data_interface": "/api/interface",
- "data_rules": "/api/rules",
- "graph": "/api/graph",
- "system": "/api/system",
- "data_source": "/api/datasource",
- "data_development": "/api/development/v1",
- "data_flow": "/api/dataflow",
- "business_domain": "/api/bd",
- "data_factory": "/api/datafactory",
- "data_service": "/api/dataservice",
- "knowledge_base": "/api/knowledge",
- }
- SHORTHAND_METHODS = {"get", "post", "put", "patch", "delete"}
- EDGE_SAFE_KEY_PATTERN = (
- "^(?!.*(?:raw|rows?|sql|credential|secret|private|password|token|local_path))"
- "[a-z][a-z0-9_]{0,79}$"
- )
- def _edge_safe_schema_lines() -> list[str]:
- lines = [
- " EdgeSafeSummary:",
- " type: object",
- " x-max-policy-depth: 12",
- " maxProperties: 64",
- f" propertyNames: {{type: string, minLength: 1, maxLength: 80, pattern: '{EDGE_SAFE_KEY_PATTERN}'}}",
- " additionalProperties: {$ref: '#/components/schemas/EdgeSafeValue12'}",
- " description: 'Policy-approved bounded metadata/statistics/health/diagnostic fields only; raw rows, SQL, credentials, and local paths are forbidden.'",
- " EdgeSafeScalar:",
- " anyOf:",
- " - {type: string, maxLength: 4096}",
- " - {type: integer, minimum: -9007199254740991, maximum: 9007199254740991}",
- " - {type: number, minimum: -1000000000000, maximum: 1000000000000}",
- " - {type: boolean}",
- " - {type: 'null'}",
- " EdgeSafeValue0: {$ref: '#/components/schemas/EdgeSafeScalar'}",
- ]
- for depth in range(1, 13):
- previous = depth - 1
- lines.extend(
- [
- f" EdgeSafeValue{depth}:",
- " anyOf:",
- " - {$ref: '#/components/schemas/EdgeSafeScalar'}",
- f" - {{type: array, maxItems: 100, items: {{$ref: '#/components/schemas/EdgeSafeValue{previous}'}}}}",
- f" - {{type: object, maxProperties: 32, propertyNames: {{type: string, minLength: 1, maxLength: 80, pattern: '{EDGE_SAFE_KEY_PATTERN}'}}, additionalProperties: {{$ref: '#/components/schemas/EdgeSafeValue{previous}'}}}}",
- ]
- )
- lines.append(" EdgeSafeValue: {$ref: '#/components/schemas/EdgeSafeValue12'}")
- return lines
- RESPONSE_FIELDS = {
- ("data_development", "summary"): [
- "scope",
- "metrics",
- ],
- ("data_development", "details"): [
- "metric",
- "state",
- "records",
- "page",
- "page_size",
- "total",
- ],
- ("knowledge_base", "ask"): [
- "query_id",
- "mode",
- "answer",
- "answer_status",
- "degraded_components",
- "citations",
- "evidence",
- "freshness_status",
- ],
- }
- CONNECTOR_REQUEST_SCHEMAS = {
- "/api/datasource/connectors/config/validate": "ConnectorConfigValidationRequest",
- "/api/datasource/connectors/runs": "ConnectorRunRequest",
- "/api/datasource/connectors/machine/runs": "MachineConnectorRunRequest",
- "/api/datasource/connectors/source-bindings": "ConnectorSourceBindingRequest",
- "/api/datasource/connectors/principals": "ConnectorPrincipalRequest",
- "/api/datasource/connectors/{connector_id}/{version}/health": "ConnectorHealthRequest",
- "/api/datasource/connectors/principals/{principal_uid}/credentials": "ConnectorCredentialRequest",
- "/api/datasource/connectors/credentials/{credential_uid}/{action}": "ConnectorCredentialRequest",
- "/api/datasource/graph": "ConnectorGraphRequest",
- }
- CONNECTOR_RESPONSE_SCHEMAS = {
- ("/api/datasource/connectors/manifests", "get"): "ConnectorManifestEnvelope",
- ("/api/datasource/connectors/runs", "get"): "ConnectorRunListEnvelope",
- ("/api/datasource/connectors/runs", "post"): "ConnectorRunResultEnvelope",
- ("/api/datasource/connectors/machine/runs", "post"): "ConnectorRunResultEnvelope",
- (
- "/api/datasource/connectors/runs/{idempotency_key}/cancel",
- "post",
- ): "ConnectorRunRecordEnvelope",
- (
- "/api/datasource/connectors/runs/{idempotency_key}/resume",
- "post",
- ): "ConnectorRunResultEnvelope",
- (
- "/api/datasource/connectors/config/validate",
- "post",
- ): "ConnectorValidationEnvelope",
- (
- "/api/datasource/connectors/{connector_id}/{version}/health",
- "post",
- ): "ConnectorHealthEnvelope",
- (
- "/api/datasource/connectors/{connector_id}/{version}/compatibility",
- "get",
- ): "ConnectorCompatibilityEnvelope",
- ("/api/datasource/connectors/principals", "post"): "ConnectorPrincipalEnvelope",
- (
- "/api/datasource/connectors/source-bindings",
- "post",
- ): "ConnectorSourceBindingEnvelope",
- (
- "/api/datasource/connectors/principals/{principal_uid}/credentials",
- "post",
- ): "ConnectorCredentialEnvelope",
- (
- "/api/datasource/connectors/credentials/{credential_uid}/{action}",
- "post",
- ): "ConnectorCredentialEnvelope",
- ("/api/datasource/graph", "post"): "ConnectorGraphEnvelope",
- }
- NO_REQUEST_BODY_PATHS = {
- "/api/datasource/connectors/runs/{idempotency_key}/cancel",
- "/api/datasource/connectors/runs/{idempotency_key}/resume",
- "/api/datasource/connectors/machine/runs/{idempotency_key}/cancel",
- "/api/datasource/connectors/machine/runs/{idempotency_key}/resume",
- "/api/datasource/connectors/source-bindings/{binding_uid}/revoke",
- "/api/datasource/edge/tasks/{task_id}/cancel",
- "/api/datasource/edge/gateways/{gateway_id}/revoke",
- }
- OPTIONAL_REQUEST_BODY_PATHS = {
- "/api/datasource/connectors/principals/{principal_uid}/credentials",
- "/api/datasource/connectors/credentials/{credential_uid}/{action}",
- }
- EDGE_REQUEST_SCHEMAS = {
- "/api/datasource/edge/enrollments": "EdgeEnrollmentRequest",
- "/api/datasource/edge/register": "EdgeRegisterRequest",
- "/api/datasource/edge/gateways/{gateway_id}/heartbeat": "EdgeHeartbeatRequest",
- "/api/datasource/edge/gateways/{gateway_id}/rotate": "EdgeRotateRequest",
- "/api/datasource/edge/tasks": "EdgeTaskContract",
- "/api/datasource/edge/gateways/{gateway_id}/tasks/pull": "EdgeMachineBinding",
- "/api/datasource/edge/gateways/{gateway_id}/tasks/{task_id}/outcome": "EdgeTaskOutcomeRequest",
- "/api/datasource/edge/gateways/{gateway_id}/events": "EdgeEventRequest",
- "/api/datasource/edge/gateways/{gateway_id}/reconcile": "EdgeReconcileRequest",
- "/api/datasource/edge/gateways/{gateway_id}/releases": "EdgeReleaseOfferRequest",
- "/api/datasource/edge/gateways/{gateway_id}/releases/ack": "EdgeReleaseAckRequest",
- }
- EDGE_RESPONSE_SCHEMAS = {
- ("/api/datasource/edge/enrollments", "post"): "EdgeEnrollmentEnvelope",
- ("/api/datasource/edge/register", "post"): "EdgeRegistrationEnvelope",
- ("/api/datasource/edge/gateways", "get"): "EdgeGatewayListEnvelope",
- (
- "/api/datasource/edge/gateways/{gateway_id}/tasks/pull",
- "post",
- ): "EdgeTaskPullEnvelope",
- (
- "/api/datasource/edge/gateways/{gateway_id}/events",
- "post",
- ): "EdgeEventAckEnvelope",
- (
- "/api/datasource/edge/gateways/{gateway_id}/reconcile",
- "post",
- ): "EdgeReconcileResponse",
- (
- "/api/datasource/edge/gateways/{gateway_id}/releases",
- "post",
- ): "EdgeReleaseEnvelope",
- }
- PRODUCTION_OBSERVABILITY_REQUEST_SCHEMAS = {
- "/api/datafactory/observability/operations/deliveries": "ProductionDeliveryRequest",
- }
- PRODUCTION_OBSERVABILITY_RESPONSE_SCHEMAS = {
- (
- "/api/datafactory/observability/operations/deliveries",
- "post",
- ): "ProductionDeliveryEnvelope",
- }
- TRUSTED_DELIVERY_PERMISSIONS = {
- "/api/system/trusted-delivery/policy-versions": "security-governance:operate",
- "/api/system/trusted-delivery/policy-versions/activate": "security-governance:operate",
- "/api/system/trusted-delivery/policy-versions/rollback": "security-governance:operate",
- "/api/system/trusted-delivery/decisions": "security-governance:operate",
- "/api/system/trusted-delivery/provisions": "security-governance:operate",
- "/api/system/trusted-delivery/legal-holds": "security-governance:manage",
- "/api/system/trusted-delivery/legal-holds/release": "security-governance:manage",
- "/api/system/trusted-delivery/reclaim/preview": "security-governance:manage",
- "/api/system/trusted-delivery/reclaim/execute": "security-governance:manage",
- "/api/system/trusted-delivery/subscriptions": "security-governance:operate",
- "/api/system/trusted-delivery/subscriptions/{subscription_uid}/activate": "security-governance:operate",
- "/api/system/trusted-delivery/subscriptions/{subscription_uid}/pause": "security-governance:operate",
- "/api/system/trusted-delivery/subscriptions/{subscription_uid}/resume": "security-governance:operate",
- "/api/system/trusted-delivery/subscriptions/{subscription_uid}/terminate": "security-governance:operate",
- "/api/system/trusted-delivery/subscriptions/deliveries/{delivery_uid}/compensate": "security-governance:manage",
- "/api/system/trusted-delivery/subscriptions/anomalies": "security-governance:operate",
- "/api/system/trusted-delivery/controls/profiles/{profile_id}/active": "security-governance:read",
- "/api/system/trusted-delivery/controls/release-gates/evaluate": "security-governance:operate",
- "/api/system/trusted-delivery/controls/profiles/activate": "security-governance:manage",
- "/api/system/trusted-delivery/controls/profiles/rollback": "security-governance:manage",
- "/api/system/trusted-delivery/controls/capability-approvals": "security-governance:manage",
- "/api/system/trusted-delivery/controls/evidence": "security-governance:read",
- "/api/system/trusted-delivery/controls/destruction-approvals": "security-governance:manage",
- }
- TENANT_CONTROL_PERMISSIONS = {
- "/api/system/tenant/quota-claims": "identity:operate",
- "/api/system/tenant/approvals": "identity:manage",
- "/api/system/tenant/provisions": "identity:manage",
- "/api/system/tenant/lifecycle/activate": "identity:manage",
- "/api/system/tenant/lifecycle/freeze": "identity:manage",
- "/api/system/tenant/lifecycle/recover": "identity:manage",
- "/api/system/tenant/lifecycle/deletion-candidate": "identity:manage",
- "/api/system/tenant/lifecycle/rollback": "identity:manage",
- "/api/system/tenant/lifecycle/delete": "identity:manage",
- "/api/system/tenant/status": "identity:read",
- "/api/system/tenant/audit": "identity:read",
- "/api/system/tenant/manifests": "identity:read",
- }
- TENANT_CONTROL_BODY_FIELDS = {
- "/api/system/tenant/quota-claims": (
- ("quota_name", "string"),
- ("amount", "string"),
- ("idempotency_key", "string"),
- ),
- "/api/system/tenant/provisions": (("idempotency_key", "string"),),
- "/api/system/tenant/approvals": (
- ("operation", "string"),
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- "/api/system/tenant/lifecycle/activate": (
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("approval_ref", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- "/api/system/tenant/lifecycle/freeze": (
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("approval_ref", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- "/api/system/tenant/lifecycle/recover": (
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("approval_ref", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- "/api/system/tenant/lifecycle/deletion-candidate": (
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("approval_ref", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- "/api/system/tenant/lifecycle/rollback": (
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("approval_ref", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- "/api/system/tenant/lifecycle/delete": (
- ("expected_fence", "string"),
- ("idempotency_key", "string"),
- ("approval_ref", "string"),
- ("backup_digest", "string"),
- ("retention_seconds", "string"),
- ),
- }
- TENANT_CONTROL_REQUIRED_FIELDS = {
- path: tuple(field for field, _ in fields)
- for path, fields in TENANT_CONTROL_BODY_FIELDS.items()
- if "/lifecycle/" not in path
- }
- TENANT_CONTROL_REQUIRED_FIELDS.update(
- {
- path: ("expected_fence", "idempotency_key")
- for path in TENANT_CONTROL_BODY_FIELDS
- if "/lifecycle/" in path
- }
- )
- TENANT_CONTROL_REQUIRED_FIELDS["/api/system/tenant/approvals"] = (
- "operation",
- "expected_fence",
- "idempotency_key",
- )
- BI_AI_CATALOG_PERMISSIONS = {
- "/api/system/bi-ai-catalog/local-fixture/sync": "bi-ai-catalog:manage",
- "/api/system/bi-ai-catalog/assets/search": "bi-ai-catalog:read",
- "/api/system/bi-ai-catalog/assets/{external_uid}/impact": "bi-ai-catalog:read",
- "/api/system/bi-ai-catalog/audit": "bi-ai-catalog:read",
- }
- METERING_SHOWBACK_PERMISSIONS = {
- "/api/system/metering/events": "metering:manage",
- "/api/system/metering/allocations": "metering:manage",
- "/api/system/metering/budgets": "metering:manage",
- "/api/system/metering/allocation-replay": "metering:read",
- "/api/system/metering/showback": "metering:read",
- "/api/system/metering/reconciliation": "metering:read",
- }
- METERING_SHOWBACK_BODY_FIELDS = {
- "/api/system/metering/events": (
- ("schema_version", "integer"), ("event_uid", "string"), ("event_kind", "string"),
- ("occurred_at", "string"), ("window_start", "string"), ("window_end", "string"),
- ("quantity", "string"), ("unit", "string"), ("idempotency_key", "string"),
- ("evidence", "object"), ("mapping", "object"),
- ),
- "/api/system/metering/allocations": (
- ("schema_version", "integer"), ("rule_uid", "string"), ("rule_version", "integer"),
- ("effective_start", "string"), ("effective_end", "string"), ("mapping", "object"), ("allocations", "array"),
- ),
- "/api/system/metering/budgets": (
- ("schema_version", "integer"), ("budget_uid", "string"), ("window", "string"),
- ("mapping", "object"), ("limit_micros", "integer"), ("threshold_micros", "integer"),
- ),
- }
- METERING_SHOWBACK_REQUIRED_FIELDS = {
- "/api/system/metering/events": tuple(field for field, _ in METERING_SHOWBACK_BODY_FIELDS["/api/system/metering/events"]),
- "/api/system/metering/allocations": tuple(field for field, _ in METERING_SHOWBACK_BODY_FIELDS["/api/system/metering/allocations"]),
- "/api/system/metering/budgets": tuple(field for field, _ in METERING_SHOWBACK_BODY_FIELDS["/api/system/metering/budgets"]),
- }
- METERING_SHOWBACK_QUERY_PARAMS = {
- "/api/system/metering/showback": (("window", "string"),),
- "/api/system/metering/reconciliation": (("window", "string"),),
- "/api/system/metering/allocation-replay": (("window", "string"), ("rule_uid", "string"), ("rule_version", "integer")),
- }
- BI_AI_CATALOG_BODY_FIELDS = {
- "/api/system/bi-ai-catalog/local-fixture/sync": (),
- "/api/system/bi-ai-catalog/assets/search": (
- ("query", "string"),
- ("filters", "object"),
- ),
- }
- BI_AI_CATALOG_REQUIRED_FIELDS = {
- "/api/system/bi-ai-catalog/local-fixture/sync": (),
- "/api/system/bi-ai-catalog/assets/search": ("query", "filters"),
- }
- EDGE_MACHINE_PATHS = {
- "/api/datasource/edge/gateways/{gateway_id}/heartbeat",
- "/api/datasource/edge/gateways/{gateway_id}/tasks/pull",
- "/api/datasource/edge/gateways/{gateway_id}/tasks/{task_id}/outcome",
- "/api/datasource/edge/gateways/{gateway_id}/events",
- "/api/datasource/edge/gateways/{gateway_id}/reconcile",
- "/api/datasource/edge/gateways/{gateway_id}/releases/ack",
- }
- EDGE_CREATED_PATHS = {
- "/api/datasource/edge/enrollments",
- "/api/datasource/edge/register",
- "/api/datasource/edge/tasks",
- "/api/datasource/edge/gateways/{gateway_id}/releases",
- }
- def quoted(value: str) -> str:
- return json.dumps(value, ensure_ascii=False)
- def route_path(raw_path: str) -> tuple[str, list[dict[str, str]]]:
- parameters: list[dict[str, str]] = []
- def replace(match: re.Match[str]) -> str:
- converter = match.group(1) or "string"
- name = match.group(2)
- schema_type = "integer" if converter in {"int", "float"} else "string"
- parameters.append({"name": name, "type": schema_type})
- return "{" + name + "}"
- return re.sub(
- r"<(?:([a-zA-Z_]+):)?([a-zA-Z_][a-zA-Z0-9_]*)>", replace, raw_path
- ), parameters
- def extract_routes() -> list[dict[str, object]]:
- routes: list[dict[str, object]] = []
- for module, prefix in PREFIXES.items():
- for route_file in sorted((ROOT / "app" / "api" / module).glob("*.py")):
- if route_file.name == "__init__.py":
- continue
- tree = ast.parse(route_file.read_text(encoding="utf-8"))
- for node in tree.body:
- if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
- continue
- for decorator in node.decorator_list:
- if not isinstance(decorator, ast.Call):
- continue
- function = decorator.func
- if not (
- isinstance(function, ast.Attribute)
- and isinstance(function.value, ast.Name)
- and function.value.id == "bp"
- and decorator.args
- ):
- continue
- if function.attr == "route":
- methods = ["GET"]
- for keyword in decorator.keywords:
- if keyword.arg == "methods":
- methods = ast.literal_eval(keyword.value)
- elif function.attr in SHORTHAND_METHODS:
- methods = [function.attr.upper()]
- else:
- continue
- raw_path = ast.literal_eval(decorator.args[0])
- path, parameters = route_path(prefix + raw_path)
- summary = (
- ast.get_docstring(node) or node.name.replace("_", " ")
- ).splitlines()[0]
- for method in methods:
- routes.append(
- {
- "path": path,
- "method": method.lower(),
- "tag": module,
- "operation_id": f"{module}_{node.name}_{method.lower()}",
- "summary": summary,
- "parameters": parameters,
- "source": str(route_file.relative_to(ROOT)),
- }
- )
- return sorted(routes, key=lambda item: (str(item["path"]), str(item["method"])))
- def render(routes: list[dict[str, object]]) -> str:
- by_path: dict[str, list[dict[str, object]]] = defaultdict(list)
- for route in routes:
- by_path[str(route["path"])].append(route)
- lines = [
- "openapi: 3.1.0",
- "info:",
- ' title: "DataOps Platform API(当前代码基线)"',
- ' version: "2026-08-09"',
- ' description: "由 scripts/generate_openapi.py 从 app/api/*/routes.py 生成。请求体与响应细节仍以现有专项 API 文档和代码为准。"',
- f"x-route-count: {len(routes)}",
- "servers:",
- ' - url: "http://localhost:15500"',
- ' description: "全本地隔离测试后端"',
- "tags:",
- ]
- for tag in PREFIXES:
- lines.extend([f" - name: {tag}", f" description: {quoted(PREFIXES[tag])}"])
- lines.append("paths:")
- for path, operations in sorted(by_path.items()):
- lines.append(f" {quoted(path)}:")
- for operation in operations:
- lines.extend(
- [
- f" {operation['method']}:",
- f" tags: [{operation['tag']}]",
- f" operationId: {operation['operation_id']}",
- f" summary: {quoted(str(operation['summary']))}",
- f" x-source: {quoted(str(operation['source']))}",
- ]
- )
- if path.startswith("/api/datasource/edge/"):
- lines.extend(
- [
- " x-max-request-bytes: 262144",
- " x-max-json-depth: 32",
- " x-max-json-nodes: 5000",
- ]
- )
- parameters = operation["parameters"]
- if path.startswith("/api/datasource/connectors/machine/runs"):
- parameters = [
- *parameters,
- {
- "name": "X-Connector-Credential",
- "type": "string",
- "header": True,
- },
- ]
- if path == "/api/datasource/edge/register":
- parameters = [
- *parameters,
- {"name": "X-Edge-Enrollment", "type": "string", "header": True},
- {
- "name": "X-DataOps-Edge-Client-Verify",
- "type": "string",
- "header": True,
- },
- {
- "name": "X-DataOps-Edge-Client-Cert",
- "type": "string",
- "header": True,
- },
- {
- "name": "X-Edge-Certificate-SHA256",
- "type": "string",
- "header": True,
- },
- ]
- elif path in EDGE_MACHINE_PATHS:
- parameters = [
- *parameters,
- {"name": "X-Edge-Credential", "type": "string", "header": True},
- {
- "name": "X-DataOps-Edge-Client-Verify",
- "type": "string",
- "header": True,
- },
- {
- "name": "X-DataOps-Edge-Client-Cert",
- "type": "string",
- "header": True,
- },
- {
- "name": "X-Edge-Certificate-SHA256",
- "type": "string",
- "header": True,
- },
- ]
- if path == "/api/datasource/edge/gateways" and operation["method"] == "get":
- parameters = [
- *parameters,
- {"name": "limit", "type": "integer", "query": True},
- {"name": "offset", "type": "integer", "query": True},
- ]
- if path in METERING_SHOWBACK_QUERY_PARAMS:
- parameters = [
- *parameters,
- *(
- {"name": name, "type": schema_type, "query": True, "required": True}
- for name, schema_type in METERING_SHOWBACK_QUERY_PARAMS[path]
- ),
- ]
- if parameters:
- lines.append(" parameters:")
- for parameter in parameters:
- lines.extend(
- [
- f" - name: {parameter['name']}",
- f" in: {'header' if parameter.get('header') else 'query' if parameter.get('query') else 'path'}",
- f" required: {'true' if parameter.get('required') else 'false' if parameter.get('query') else 'true'}",
- " schema:",
- f" type: {parameter['type']}",
- ]
- )
- if parameter["name"] in {"X-Edge-Credential", "X-Edge-Enrollment"}:
- lines.extend(
- [
- " format: password",
- " writeOnly: true",
- ]
- )
- if parameter["name"] == "limit":
- lines.extend(
- [" minimum: 1", " maximum: 100"]
- )
- if parameter["name"] == "offset":
- lines.extend(
- [" minimum: 0", " maximum: 10000"]
- )
- if (
- path
- == "/api/datasource/connectors/credentials/{credential_uid}/{action}"
- and parameter["name"] == "action"
- ):
- lines.append(" enum: [revoke, rotate]")
- if (
- operation["method"] in {"post", "put", "patch"}
- and path not in NO_REQUEST_BODY_PATHS
- ):
- connector_schema = CONNECTOR_REQUEST_SCHEMAS.get(path)
- request_schema = (
- connector_schema
- or EDGE_REQUEST_SCHEMAS.get(path)
- or PRODUCTION_OBSERVABILITY_REQUEST_SCHEMAS.get(path)
- )
- lines.extend(
- [
- " requestBody:",
- " required: "
- + (
- "false"
- if path in OPTIONAL_REQUEST_BODY_PATHS
- else "true"
- if request_schema
- or path in TENANT_CONTROL_BODY_FIELDS
- or path in BI_AI_CATALOG_BODY_FIELDS
- or path in METERING_SHOWBACK_BODY_FIELDS
- else "false"
- ),
- " content:",
- " application/json:",
- " schema:",
- f" $ref: '#/components/schemas/{request_schema}'"
- if request_schema
- else " type: object",
- ]
- )
- if not request_schema:
- lines.append(
- " additionalProperties: false"
- if path in TRUSTED_DELIVERY_PERMISSIONS
- or path in TENANT_CONTROL_PERMISSIONS
- or path in BI_AI_CATALOG_PERMISSIONS
- or path in METERING_SHOWBACK_PERMISSIONS
- else " additionalProperties: true"
- )
- if path in TENANT_CONTROL_BODY_FIELDS:
- fields = TENANT_CONTROL_BODY_FIELDS[path]
- lines.append(
- " required: ["
- + ", ".join(TENANT_CONTROL_REQUIRED_FIELDS[path])
- + "]"
- )
- lines.append(" properties:")
- for field, field_type in fields:
- lines.extend(
- [
- f" {field}:",
- f" type: {field_type}",
- ]
- )
- if (
- path == "/api/system/tenant/quota-claims"
- and field == "amount"
- ):
- lines.append(
- " pattern: '^[0-9]+([.][0-9]{1,6})?$'"
- )
- if path in BI_AI_CATALOG_BODY_FIELDS:
- fields = BI_AI_CATALOG_BODY_FIELDS[path]
- lines.append(
- " required: ["
- + ", ".join(BI_AI_CATALOG_REQUIRED_FIELDS[path])
- + "]"
- )
- if fields:
- lines.append(" properties:")
- for field, field_type in fields:
- lines.extend(
- [
- f" {field}:",
- f" type: {field_type}",
- ]
- )
- if path in METERING_SHOWBACK_BODY_FIELDS:
- fields = METERING_SHOWBACK_BODY_FIELDS[path]
- lines.append(" required: [" + ", ".join(METERING_SHOWBACK_REQUIRED_FIELDS[path]) + "]")
- lines.append(" properties:")
- for field, field_type in fields:
- lines.extend([f" {field}:", f" type: {field_type}"])
- if field == "mapping":
- lines.extend([" additionalProperties: false", " required: [department, project, cost_center]", " properties:", " department: {type: string}", " project: {type: string}", " cost_center: {type: string}"])
- elif field == "evidence":
- lines.extend([" additionalProperties: false", " required: [digest, reference]", " properties:", " digest: {type: string}", " reference: {type: string}"])
- elif field == "allocations":
- lines.extend([" minItems: 1", " maxItems: 32", " items:", " type: object", " additionalProperties: false", " required: [target, weight_micros]", " properties:", " target: {type: string}", " weight_micros: {type: integer, minimum: 1, maximum: 1000000}"])
- response_fields = RESPONSE_FIELDS.get(
- (str(operation["tag"]), str(operation["operation_id"]).split("_")[-2])
- )
- if response_fields:
- lines.append(
- " x-response-fields: [" + ", ".join(response_fields) + "]"
- )
- connector_response = CONNECTOR_RESPONSE_SCHEMAS.get(
- (path, str(operation["method"]))
- )
- edge_response = EDGE_RESPONSE_SCHEMAS.get((path, str(operation["method"])))
- response_status = (
- "201"
- if operation["method"] == "post"
- and (
- path in EDGE_CREATED_PATHS
- or path
- in {
- "/api/system/bi-ai-catalog/local-fixture/sync",
- "/api/datasource/connectors/runs",
- "/api/datasource/connectors/machine/runs",
- "/api/datasource/connectors/principals",
- "/api/datasource/connectors/principals/{principal_uid}/credentials",
- "/api/datasource/connectors/source-bindings",
- "/api/datafactory/observability/operations/deliveries",
- "/api/system/tenant/quota-claims",
- "/api/system/tenant/provisions",
- "/api/system/tenant/approvals",
- "/api/system/metering/events",
- "/api/system/metering/allocations",
- "/api/system/metering/budgets",
- }
- )
- else "200"
- )
- if path.startswith("/api/datasource/connectors"):
- required_permission = (
- "machine-credential"
- if path.startswith("/api/datasource/connectors/machine/runs")
- else (
- "connectors:manage"
- if "/source-bindings" in path
- else (
- "connectors:read"
- if operation["method"] == "get"
- else (
- "connectors:manage"
- if "/principals" in path or "/credentials" in path
- else "connectors:operate"
- )
- )
- )
- )
- lines.append(
- f" x-required-permission: {quoted(required_permission)}"
- )
- elif path == "/api/datasource/graph":
- lines.append(' x-required-permission: "connectors:read"')
- elif path == "/api/datafactory/observability/operations/deliveries":
- lines.append(
- ' x-required-permission: "data-observability:operate"'
- )
- elif path in TRUSTED_DELIVERY_PERMISSIONS:
- lines.append(
- f" x-required-permission: {quoted(TRUSTED_DELIVERY_PERMISSIONS[path])}"
- )
- elif path in TENANT_CONTROL_PERMISSIONS:
- lines.append(
- f" x-required-permission: {quoted(TENANT_CONTROL_PERMISSIONS[path])}"
- )
- elif path in BI_AI_CATALOG_PERMISSIONS:
- lines.append(
- f" x-required-permission: {quoted(BI_AI_CATALOG_PERMISSIONS[path])}"
- )
- elif path in METERING_SHOWBACK_PERMISSIONS:
- lines.append(f" x-required-permission: {quoted(METERING_SHOWBACK_PERMISSIONS[path])}")
- elif path.startswith("/api/datasource/edge"):
- if path == "/api/datasource/edge/register":
- permission = "one-time-enrollment+mTLS"
- elif path in EDGE_MACHINE_PATHS:
- permission = "edge-credential+mTLS"
- elif (
- path == "/api/datasource/edge/gateways"
- and operation["method"] == "get"
- ):
- permission = "edge-gateways:read"
- elif path == "/api/datasource/edge/enrollments":
- permission = "edge-gateways:manage"
- else:
- permission = "edge-gateways:operate"
- lines.append(f" x-required-permission: {quoted(permission)}")
- if path == "/api/datasource/edge/register":
- lines.extend(
- [
- " security:",
- " - mutualTLS: []",
- " edgeEnrollment: []",
- ]
- )
- elif path in EDGE_MACHINE_PATHS:
- lines.extend(
- [
- " security:",
- " - mutualTLS: []",
- " edgeCredential: []",
- ]
- )
- else:
- lines.extend([" security:", " - bearerAuth: []"])
- success_headers = (
- [
- " headers:",
- " Cache-Control:",
- " required: true",
- " schema: {type: string, const: no-store}",
- ]
- if path.startswith("/api/datasource/edge")
- or path == "/api/datafactory/observability/operations/deliveries"
- or path in TRUSTED_DELIVERY_PERMISSIONS
- or path in TENANT_CONTROL_PERMISSIONS
- or path in BI_AI_CATALOG_PERMISSIONS
- or path in METERING_SHOWBACK_PERMISSIONS
- else []
- )
- lines.extend(
- [
- " responses:",
- f' "{response_status}":',
- ' description: "请求已由当前实现处理"',
- *success_headers,
- " content:",
- " application/json:",
- " schema:",
- f" $ref: '#/components/schemas/{connector_response or edge_response or PRODUCTION_OBSERVABILITY_RESPONSE_SCHEMAS.get((path, str(operation['method']))) or ('EdgeApiEnvelope' if path.startswith('/api/datasource/edge') else 'ApiEnvelope')}'",
- " default:",
- ' description: "错误响应"',
- *success_headers,
- " content:",
- " application/json:",
- " schema:",
- f" $ref: '#/components/schemas/{'ConnectorErrorEnvelope' if path.startswith('/api/datasource/connectors') or path == '/api/datasource/graph' else 'EdgeErrorEnvelope' if path.startswith('/api/datasource/edge') else 'ApiEnvelope'}'",
- ]
- )
- lines.extend(
- [
- "components:",
- " schemas:",
- " ApiEnvelope:",
- " type: object",
- " additionalProperties: true",
- " required: [code, message, data]",
- " properties:",
- " code:",
- " type: integer",
- " message:",
- " type: string",
- " data: {}",
- " error: {type: object}",
- " EdgeApiEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object}}",
- " ProductionDeliveryRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [incident_uid, alert_uid, channel, summary]",
- " properties: {incident_uid: {type: string, format: uuid}, alert_uid: {type: string, format: uuid}, channel: {type: string, enum: [monitoring, smtp, enterprise_collaboration, on_call, itsm]}, summary: {type: string, minLength: 1, maxLength: 160}}",
- " ProductionDeliveryRecord:",
- " type: object",
- " additionalProperties: false",
- " required: [uid, incident_uid, alert_uid, channel, status, attempt_count, lease_fence]",
- " properties: {uid: {type: string, format: uuid}, incident_uid: {type: string, format: uuid}, alert_uid: {type: string, format: uuid}, channel: {type: string, enum: [monitoring, smtp, enterprise_collaboration, on_call, itsm]}, status: {type: string, enum: [pending, processing, delivered, dead_letter, compensated]}, attempt_count: {type: integer, minimum: 0, maximum: 3}, lease_fence: {type: integer, minimum: 0}, next_attempt_at: {type: string, format: date-time}, lease_owner: {type: [string, 'null'], maxLength: 80}, lease_expires_at: {type: [string, 'null'], format: date-time}, last_reason_code: {type: [string, 'null'], maxLength: 64}, compensation_reason_code: {type: [string, 'null'], maxLength: 64}, compensation_receipt_code: {type: [string, 'null'], maxLength: 64}, idempotency_key: {type: string, maxLength: 200}, payload_digest: {type: string, pattern: '^[a-f0-9]{64}$'}, created_at: {type: string, format: date-time}, updated_at: {type: string, format: date-time}}",
- " ProductionDeliveryEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {const: 200}, message: {type: string}, data: {$ref: '#/components/schemas/ProductionDeliveryRecord'}}",
- " EdgeErrorEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data, error]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: 'null'}, error: {type: object, additionalProperties: false, required: [code], properties: {code: {type: string, maxLength: 80}}}}",
- " EdgeMachineBinding:",
- " type: object",
- " additionalProperties: false",
- " required: [environment, network_zone, generation]",
- " properties: {environment: {type: string, enum: [development, staging, production]}, network_zone: {type: string, minLength: 1, maxLength: 120}, generation: {type: integer, minimum: 1}}",
- " EdgeEnrollmentRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [gateway_name, environment, network_zone, policy_digest, allowed_control_hosts, allowed_proxy_hosts, expected_certificate_sha256]",
- " properties: {gateway_name: {type: string, minLength: 1, maxLength: 200}, environment: {type: string, enum: [development, staging, production]}, network_zone: {type: string, minLength: 1, maxLength: 120}, policy_digest: {$ref: '#/components/schemas/Sha256Digest'}, allowed_control_hosts: {$ref: '#/components/schemas/EdgeExactHosts'}, allowed_proxy_hosts: {$ref: '#/components/schemas/EdgeExactHosts'}, expected_certificate_sha256: {$ref: '#/components/schemas/Sha256Digest'}, ttl_seconds: {type: integer, minimum: 60, maximum: 86400}}",
- " EdgeRegisterRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [gateway_id, environment, network_zone, policy_digest, allowed_control_hosts, allowed_proxy_hosts, version]",
- " properties: {gateway_id: {type: string, format: uuid}, environment: {type: string, enum: [development, staging, production]}, network_zone: {type: string, minLength: 1, maxLength: 120}, policy_digest: {$ref: '#/components/schemas/Sha256Digest'}, allowed_control_hosts: {$ref: '#/components/schemas/EdgeExactHosts'}, allowed_proxy_hosts: {$ref: '#/components/schemas/EdgeExactHosts'}, version: {$ref: '#/components/schemas/EdgeAgentVersion'}}",
- " EdgeHeartbeatRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [environment, network_zone, generation, version, safe_summary]",
- " properties: {environment: {type: string, enum: [development, staging, production]}, network_zone: {type: string, minLength: 1, maxLength: 120}, generation: {type: integer, minimum: 1}, version: {$ref: '#/components/schemas/EdgeAgentVersion'}, safe_summary: {$ref: '#/components/schemas/EdgeSafeSummary'}}",
- " EdgeRotateRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [certificate_sha256, request_id]",
- " properties: {certificate_sha256: {$ref: '#/components/schemas/Sha256Digest'}, request_id: {type: string, minLength: 1, maxLength: 255}}",
- " Sha256Digest: {type: string, pattern: '^[a-f0-9]{64}$'}",
- " Ed25519Signature: {type: string, pattern: '^[a-f0-9]{128}$'}",
- " EdgeExactHosts: {type: array, maxItems: 32, uniqueItems: true, items: {type: string, pattern: '^[a-z0-9][a-z0-9.-]*[a-z0-9]$'}}",
- *_edge_safe_schema_lines(),
- " EdgeIdentifier: {type: string, minLength: 1, maxLength: 255, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,254}$'}",
- " EdgeCursor: {type: [string, 'null'], minLength: 1, maxLength: 255, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,254}$'}",
- " EdgeAgentVersion: {type: string, minLength: 1, maxLength: 80, pattern: '^[A-Za-z0-9][A-Za-z0-9._+-]{0,79}$'}",
- " EdgeVersion: {type: string, pattern: '^(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})$'}",
- " EdgeTaskContract:",
- " type: object",
- " additionalProperties: false",
- " required: [task_id, gateway_id, environment, network_zone, purpose, classification, task_type, contract_version, deadline_at, attempt, idempotency_key, policy_digest]",
- " properties: {task_id: {$ref: '#/components/schemas/EdgeIdentifier'}, gateway_id: {type: string, format: uuid}, environment: {type: string, enum: [development, staging, production]}, network_zone: {type: string, minLength: 1, maxLength: 120}, purpose: {type: string, minLength: 1, maxLength: 255}, classification: {type: string, enum: [raw, recent_detail, restricted, desensitized_metadata, statistics, lineage, evidence]}, task_type: {type: string, enum: [collect, profile, quality, lineage, controlled_query]}, contract_version: {const: 1}, deadline_at: {type: string, format: date-time}, attempt: {type: integer, minimum: 1, maximum: 5}, idempotency_key: {$ref: '#/components/schemas/EdgeIdentifier'}, policy_digest: {$ref: '#/components/schemas/Sha256Digest'}}",
- " EdgeSignedTaskEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [task, authority_key_id, signature_algorithm, contract_digest, gateway_id, environment, network_zone, policy_digest, purpose, issued_at, expires_at, signature]",
- " properties: {task: {$ref: '#/components/schemas/EdgeTaskContract'}, authority_key_id: {type: string}, signature_algorithm: {const: Ed25519}, contract_digest: {$ref: '#/components/schemas/Sha256Digest'}, gateway_id: {type: string}, environment: {type: string}, network_zone: {type: string}, policy_digest: {$ref: '#/components/schemas/Sha256Digest'}, purpose: {type: string}, issued_at: {type: string, format: date-time}, expires_at: {type: string, format: date-time}, signature: {$ref: '#/components/schemas/Ed25519Signature'}}",
- " EdgeEventContract:",
- " type: object",
- " additionalProperties: false",
- " required: [event_id, task_id, gateway_id, environment, network_zone, purpose, classification, contract_version, occurred_at, attempt, idempotency_key, policy_digest, payload]",
- " properties: {event_id: {type: string}, task_id: {type: string}, gateway_id: {type: string}, environment: {type: string}, network_zone: {type: string}, purpose: {type: string}, classification: {type: string, enum: [desensitized_metadata, statistics, lineage, evidence, health_summary, diagnostic_summary]}, contract_version: {const: 1}, occurred_at: {type: string, format: date-time}, attempt: {type: integer, minimum: 1, maximum: 5}, idempotency_key: {type: string}, policy_digest: {$ref: '#/components/schemas/Sha256Digest'}, payload: {$ref: '#/components/schemas/EdgeSafeSummary'}}",
- " EdgeEventRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [environment, network_zone, generation, event, lease_token]",
- " properties: {environment: {type: string}, network_zone: {type: string}, generation: {type: integer, minimum: 1}, event: {$ref: '#/components/schemas/EdgeEventContract'}, lease_token: {type: string, writeOnly: true}}",
- " EdgeTaskOutcomeRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [environment, network_zone, generation, outcome, lease_token, safe_summary]",
- " properties: {environment: {type: string}, network_zone: {type: string}, generation: {type: integer, minimum: 1}, outcome: {type: string, enum: [completed, failed, cancelled]}, lease_token: {type: string, writeOnly: true}, safe_summary: {$ref: '#/components/schemas/EdgeSafeSummary'}}",
- " EdgeReconcileRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [environment, network_zone, generation]",
- " properties: {environment: {type: string, enum: [development, staging, production]}, network_zone: {type: string, minLength: 1, maxLength: 120}, generation: {type: integer, minimum: 1}, limit: {type: integer, minimum: 1, maximum: 100}, cancel_cursor: {$ref: '#/components/schemas/EdgeCursor'}, release_cursor: {$ref: '#/components/schemas/EdgeCursor'}}",
- " EdgeReconcileResponse:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string, maxLength: 200}, data: {type: object, additionalProperties: false, required: [cancelled_task_ids, cancel_next_cursor, release_offers, release_next_cursor, release_baseline], properties: {cancelled_task_ids: {type: array, maxItems: 50, items: {$ref: '#/components/schemas/EdgeIdentifier'}}, cancel_next_cursor: {$ref: '#/components/schemas/EdgeCursor'}, release_offers: {type: array, maxItems: 50, items: {$ref: '#/components/schemas/EdgeReleaseManifest'}}, release_next_cursor: {$ref: '#/components/schemas/EdgeCursor'}, release_baseline: {oneOf: [{$ref: '#/components/schemas/EdgeReleaseManifest'}, {type: 'null'}]}}}}",
- " EdgeReleaseOfferRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [version, artifact_digest, artifact_name, deadline_at, rollback_version, request_id]",
- " properties: {version: {$ref: '#/components/schemas/EdgeVersion'}, artifact_digest: {$ref: '#/components/schemas/Sha256Digest'}, artifact_name: {type: string, minLength: 1, maxLength: 255, pattern: '^[^/\\\\]+$'}, deadline_at: {type: string, format: date-time}, rollback_version: {$ref: '#/components/schemas/EdgeVersion'}, request_id: {$ref: '#/components/schemas/EdgeIdentifier'}}",
- " EdgeReleaseAckRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [environment, network_zone, generation, release_id, outcome, safe_summary]",
- " properties: {environment: {type: string}, network_zone: {type: string}, generation: {type: integer, minimum: 1}, release_id: {type: string, format: uuid}, outcome: {type: string, enum: [accepted, installed, failed, rollback]}, safe_summary: {$ref: '#/components/schemas/EdgeSafeSummary'}}",
- " EdgeReleaseManifest:",
- " type: object",
- " additionalProperties: false",
- " required: [release_id, version, rollback_version, artifact_digest, artifact_name, deadline_at, status, signature_algorithm, key_id, manifest_digest, signature]",
- " properties: {release_id: {$ref: '#/components/schemas/EdgeIdentifier'}, version: {$ref: '#/components/schemas/EdgeVersion'}, rollback_version: {$ref: '#/components/schemas/EdgeVersion'}, artifact_digest: {$ref: '#/components/schemas/Sha256Digest'}, artifact_name: {type: string, minLength: 1, maxLength: 255, pattern: '^[^/\\\\]+$'}, deadline_at: {type: string, format: date-time}, status: {type: string, enum: [offered, accepted, failed, installed, rolled_back]}, signature_algorithm: {const: Ed25519}, key_id: {$ref: '#/components/schemas/EdgeIdentifier'}, manifest_digest: {$ref: '#/components/schemas/Sha256Digest'}, signature: {$ref: '#/components/schemas/Ed25519Signature'}}",
- " EdgeEnrollmentEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [enrollment_id, gateway_id, enrollment_token, expires_in, returned_once], properties: {enrollment_id: {type: string, format: uuid}, gateway_id: {type: string, format: uuid}, enrollment_token: {type: string, writeOnly: true}, expires_in: {type: integer}, returned_once: {const: true}}}}",
- " EdgeRegistrationEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [gateway_id, generation, certificate_sha256, credential, credential_returned_once], properties: {gateway_id: {type: string, format: uuid}, generation: {type: integer}, certificate_sha256: {$ref: '#/components/schemas/Sha256Digest'}, credential: {type: string, writeOnly: true}, credential_returned_once: {const: true}}}}",
- " EdgeGatewayListEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [gateways], properties: {gateways: {type: array, maxItems: 100, items: {type: object, description: 'Bounded gateway metadata; credential/token/private key are never included.'}}}}}",
- " EdgeTaskPullEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {oneOf: [{type: object, additionalProperties: false, required: [task], properties: {task: {type: 'null'}}}, {type: object, additionalProperties: false, required: [task, signed_task_envelope, lease_token, lease_expires_at], properties: {task: {$ref: '#/components/schemas/EdgeTaskContract'}, signed_task_envelope: {$ref: '#/components/schemas/EdgeSignedTaskEnvelope'}, lease_token: {type: string, writeOnly: true}, lease_expires_at: {type: string, format: date-time}}}]}}",
- " EdgeEventAckEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [event_id, event_digest, remote_lease_digest, received_at, status], properties: {event_id: {type: string}, event_digest: {$ref: '#/components/schemas/Sha256Digest'}, remote_lease_digest: {$ref: '#/components/schemas/Sha256Digest'}, received_at: {type: string, format: date-time}, status: {const: accepted}}}}",
- " EdgeReleaseEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string, maxLength: 200}, data: {type: object, additionalProperties: false, required: [release_id, status, version, artifact_digest, rollback_version, manifest], properties: {release_id: {type: string, format: uuid}, status: {type: string, enum: [offered, accepted, failed, installed, rolled_back]}, version: {$ref: '#/components/schemas/EdgeVersion'}, artifact_digest: {$ref: '#/components/schemas/Sha256Digest'}, rollback_version: {$ref: '#/components/schemas/EdgeVersion'}, manifest: {$ref: '#/components/schemas/EdgeReleaseManifest'}}}}",
- " ConnectorConfigValidationRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [connector_id, version, config]",
- " properties:",
- " connector_id: {type: string, minLength: 3, maxLength: 64}",
- " version: {type: string, pattern: '^[0-9]+\\.[0-9]+\\.[0-9]+'}",
- " config: {type: object}",
- " ConnectorRunRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [connector_id, version, source_uid, operation, config, scope, dry_run]",
- " properties:",
- " connector_id: {type: string}",
- " version: {type: string}",
- " source_uid: {type: string, format: uuid}",
- " operation: {type: string, enum: [discover, snapshot, incremental, lineage, profile, evidence]}",
- " config: {type: object}",
- " scope: {$ref: '#/components/schemas/ConnectorScope'}",
- " cursor: {type: object}",
- " checkpoint: {type: object}",
- " idempotency_key: {type: string, pattern: '^[a-f0-9]{64}$'}",
- " dry_run: {const: true}",
- " process_key: {type: string, minLength: 1, maxLength: 300}",
- " MachineConnectorRunRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [connector_id, version, source_uid, business_domain_uid, environment, process_key, operation, scope]",
- " properties:",
- " connector_id: {type: string}",
- " version: {type: string}",
- " source_uid: {type: string, format: uuid}",
- " business_domain_uid: {type: string, format: uuid}",
- " environment: {type: string, enum: [development, staging, production]}",
- " process_key: {type: string, minLength: 1, maxLength: 300}",
- " operation: {type: string, enum: [discover, snapshot, incremental, lineage, profile, evidence]}",
- " scope: {$ref: '#/components/schemas/ConnectorScope'}",
- " cursor: {type: object}",
- " checkpoint: {type: object}",
- " idempotency_key: {type: string, pattern: '^[a-f0-9]{64}$'}",
- " dry_run: {type: boolean, default: false}",
- " ConnectorPrincipalRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [connector_id, version, source_uid, business_domain_uid, environment, operations, scopes]",
- " properties:",
- " connector_id: {type: string}",
- " version: {type: string}",
- " source_uid: {type: string, format: uuid}",
- " business_domain_uid: {type: string, format: uuid}",
- " environment: {type: string, enum: [development, staging, production]}",
- " operations: {type: array, minItems: 1, uniqueItems: true, items: {type: string}}",
- " scopes: {$ref: '#/components/schemas/ConnectorScope'}",
- " source_binding_uid: {type: string, format: uuid}",
- " source_binding_version: {type: integer, minimum: 1}",
- " ConnectorSourceBindingRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [connector_id, version, source_uid, business_domain_uid, environment, approved_config]",
- " properties:",
- " binding_uid: {type: string, format: uuid}",
- " connector_id: {type: string}",
- " version: {type: string}",
- " source_uid: {type: string, format: uuid}",
- " business_domain_uid: {type: string, format: uuid}",
- " environment: {type: string, enum: [development, staging, production]}",
- " approved_config: {type: object}",
- " ConnectorScope:",
- " type: object",
- " additionalProperties: false",
- " properties:",
- " include_schemas: {type: array, uniqueItems: true, items: {type: string, minLength: 1}}",
- " exclude_schemas: {type: array, uniqueItems: true, items: {type: string, minLength: 1}}",
- " include_tables: {type: array, uniqueItems: true, items: {type: string, minLength: 1}}",
- " exclude_tables: {type: array, uniqueItems: true, items: {type: string, minLength: 1}}",
- " ConnectorHealthRequest:",
- " type: object",
- " additionalProperties: false",
- " required: [config]",
- " properties: {config: {type: object}}",
- " ConnectorCredentialRequest:",
- " type: object",
- " additionalProperties: false",
- " properties: {ttl_seconds: {type: integer, minimum: 60, maximum: 900}}",
- " ConnectorGraphRequest:",
- " type: object",
- " additionalProperties: false",
- " properties: {source_uid: {type: string, format: uuid}, business_domain_uid: {type: string, format: uuid}, process_key: {type: string}, run_uid: {type: string, format: uuid}, limit: {type: integer, minimum: 1, maximum: 1000}}",
- " ConnectorManifestEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties:",
- " code: {type: integer}",
- " message: {type: string}",
- " data: {type: object, additionalProperties: false, required: [manifests], properties: {manifests: {type: array, items: {$ref: '#/components/schemas/ConnectorManifest'}}}}",
- " ConnectorManifest:",
- " type: object",
- " additionalProperties: false",
- " required: [connector_id, version, sdk_version, display_name, capabilities, config_schema]",
- " properties: {connector_id: {type: string}, version: {type: string}, sdk_version: {type: string}, display_name: {type: string}, capabilities: {type: array, items: {type: string}}, config_schema: {type: object}}",
- " ConnectorOperationResult:",
- " type: object",
- " additionalProperties: false",
- " required: [records, cursor, checkpoint, evidence, status]",
- " properties: {records: {type: array, items: {type: object}}, cursor: {type: object}, checkpoint: {type: object}, evidence: {type: object}, status: {type: string, enum: [succeeded, dry_run]}}",
- " ConnectorRunResultEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {$ref: '#/components/schemas/ConnectorOperationResult'}}",
- " ConnectorRunRecord:",
- " type: object",
- " additionalProperties: false",
- " required: [uid, idempotency_key, connector_id, connector_version, source_uid, operation, status, attempt_count, checkpoint_summary, cursor_summary, dry_run]",
- " properties: {uid: {type: string, format: uuid}, idempotency_key: {type: string}, connector_id: {type: string}, connector_version: {type: string}, source_uid: {type: string, format: uuid}, principal_uid: {type: [string, 'null'], format: uuid}, business_domain_uid: {type: [string, 'null'], format: uuid}, environment: {type: [string, 'null']}, process_key: {type: [string, 'null']}, operation: {type: string}, status: {type: string}, attempt_count: {type: integer}, checkpoint_summary: {type: object}, cursor_summary: {type: object}, scope: {type: object}, error_category: {type: [string, 'null']}, dry_run: {type: boolean}, created_at: {type: string}, updated_at: {type: string}}",
- " ConnectorRunRecordEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {$ref: '#/components/schemas/ConnectorRunRecord'}}",
- " ConnectorRunListEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [runs], properties: {runs: {type: array, items: {$ref: '#/components/schemas/ConnectorRunRecord'}}}}}",
- " ConnectorValidationEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, required: [valid, config_keys], properties: {valid: {const: true}, config_keys: {type: array, items: {type: string}}}, additionalProperties: false}}",
- " ConnectorErrorEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data, error]",
- " properties:",
- " code: {type: integer}",
- " message: {type: string}",
- " data: {type: 'null'}",
- " error: {type: object, additionalProperties: false, required: [code, category, retryable], properties: {code: {const: CONNECTOR_ERROR}, category: {type: string, enum: [configuration, conflict, authentication, permission, rate_limit, timeout, upstream, contract, cancelled]}, retryable: {type: boolean}}}",
- " ConnectorHealthEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [status, detail], properties: {status: {type: string}, detail: {type: string}}}}",
- " ConnectorCompatibilityEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [compatible, connector_version, sdk_version, detail], properties: {compatible: {type: boolean}, connector_version: {type: string}, sdk_version: {type: string}, detail: {type: string}}}}",
- " ConnectorPrincipalEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [principal_uid], properties: {principal_uid: {type: string, format: uuid}}}}",
- " ConnectorSourceBindingEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [binding_uid, binding_version, status, rebound_principals], properties: {binding_uid: {type: string, format: uuid}, binding_version: {type: integer, minimum: 1}, status: {const: approved}, rebound_principals: {type: integer, minimum: 0}}}}",
- " ConnectorCredentialEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {oneOf: [{type: object, additionalProperties: false, required: [credential_uid, credential, expires_in, returned_once], properties: {credential_uid: {type: string, format: uuid}, credential: {type: string, writeOnly: true}, expires_in: {type: integer}, returned_once: {const: true}}}, {type: object, additionalProperties: false, required: [revoked], properties: {revoked: {type: boolean}}}]}}",
- " ConnectorGraphEnvelope:",
- " type: object",
- " additionalProperties: false",
- " required: [code, message, data]",
- " properties: {code: {type: integer}, message: {type: string}, data: {type: object, additionalProperties: false, required: [nodes, edges, summary], properties: {nodes: {type: array, items: {type: object, additionalProperties: false, required: [type, key], properties: {type: {type: string, enum: [source, asset, process, business_domain, run]}, key: {type: string}}}}, edges: {type: array, items: {type: object}}, summary: {type: object, additionalProperties: false, required: [node_count, edge_count], properties: {node_count: {type: integer}, edge_count: {type: integer}}}}}}",
- " securitySchemes:",
- " mutualTLS:",
- " type: mutualTLS",
- " edgeCredential:",
- " type: apiKey",
- " in: header",
- " name: X-Edge-Credential",
- ' description: "One-time-returned edge credential; always combined with mutualTLS certificate proof."',
- " edgeEnrollment:",
- " type: apiKey",
- " in: header",
- " name: X-Edge-Enrollment",
- ' description: "Single-use enrollment token; always combined with mutualTLS certificate proof."',
- " bearerAuth:",
- " type: http",
- " scheme: bearer",
- " bearerFormat: JWT",
- ' description: "下一阶段统一认证方案;当前路由尚未全部接入。"',
- ]
- )
- return "\n".join(lines) + "\n"
- def main(output: Path = OUTPUT) -> None:
- routes = extract_routes()
- if not routes:
- raise SystemExit("No Flask routes found")
- output.parent.mkdir(parents=True, exist_ok=True)
- output.write_text(render(routes), encoding="utf-8")
- try:
- label = output.relative_to(ROOT)
- except ValueError:
- label = output
- print(f"Generated {label} with {len(routes)} operations")
- if __name__ == "__main__":
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument("--output", type=Path, default=OUTPUT)
- args = parser.parse_args()
- main(args.output)
|