keboola-cli 0.63.4__py3-none-any.whl
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.
- keboola_agent_cli/__init__.py +34 -0
- keboola_agent_cli/__main__.py +5 -0
- keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js +2 -0
- keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/architecture-7EHR7CIX-hNCijx_H.js +1 -0
- keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js +37 -0
- keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js +2 -0
- keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js +133 -0
- keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js +11 -0
- keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js +2 -0
- keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js +63 -0
- keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js +3 -0
- keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js +207 -0
- keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js +232 -0
- keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js +124 -0
- keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js +16 -0
- keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js +160 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js +71 -0
- keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js +2 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js +2 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js +2 -0
- keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js +322 -0
- keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js +5 -0
- keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js +2 -0
- keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js +2 -0
- keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js +44 -0
- keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js +11 -0
- keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js +4 -0
- keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js +25 -0
- keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js +25 -0
- keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js +2 -0
- keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js +86 -0
- keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/eventmodeling-FCH6USID-BrJMIks8.js +1 -0
- keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js +163 -0
- keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js +293 -0
- keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/gitGraph-WXDBUCRP-COacYjo-.js +1 -0
- keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js +107 -0
- keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js +2 -0
- keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/index-CMq50kkV.css +1 -0
- keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js +118 -0
- keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/info-J43DQDTF-DdCTRIzU.js +1 -0
- keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js +3 -0
- keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js +2 -0
- keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js +71 -0
- keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js +140 -0
- keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js +90 -0
- keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js +258 -0
- keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js +2 -0
- keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js +2 -0
- keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js +5 -0
- keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js +97 -0
- keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js +2 -0
- keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/packet-YPE3B663-DLiiw_B2.js +1 -0
- keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js +2 -0
- keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/pie-LRSECV5Y-CRoO8G1g.js +1 -0
- keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js +31 -0
- keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js +8 -0
- keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/radar-GUYGQ44K-DAlLVJHm.js +1 -0
- keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js +85 -0
- keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js +2 -0
- keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js +41 -0
- keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js +163 -0
- keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js +2 -0
- keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js +2 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js +2 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js +121 -0
- keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/treeView-BLDUP644-DbLYl23-.js +1 -0
- keboola_agent_cli/_ui_dist/assets/treemap-LRROVOQU-Bp0eGlOt.js +1 -0
- keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js +35 -0
- keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/wardley-L42UT6IY-D4yH4jqS.js +1 -0
- keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js +79 -0
- keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js +8 -0
- keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/index.html +50 -0
- keboola_agent_cli/ai_client.py +83 -0
- keboola_agent_cli/auto_update.py +550 -0
- keboola_agent_cli/changelog.py +1198 -0
- keboola_agent_cli/cli.py +448 -0
- keboola_agent_cli/client.py +3422 -0
- keboola_agent_cli/commands/__init__.py +0 -0
- keboola_agent_cli/commands/_data_app_git.py +343 -0
- keboola_agent_cli/commands/_helpers.py +377 -0
- keboola_agent_cli/commands/_metadata_input.py +49 -0
- keboola_agent_cli/commands/_semantic_layer_crud.py +632 -0
- keboola_agent_cli/commands/_semantic_layer_helpers.py +44 -0
- keboola_agent_cli/commands/_semantic_layer_reference_data.py +247 -0
- keboola_agent_cli/commands/agent.py +968 -0
- keboola_agent_cli/commands/branch.py +423 -0
- keboola_agent_cli/commands/changelog.py +168 -0
- keboola_agent_cli/commands/component.py +216 -0
- keboola_agent_cli/commands/config.py +2442 -0
- keboola_agent_cli/commands/context.py +1481 -0
- keboola_agent_cli/commands/data_app.py +1279 -0
- keboola_agent_cli/commands/dev_portal.py +584 -0
- keboola_agent_cli/commands/doctor.py +37 -0
- keboola_agent_cli/commands/encrypt.py +145 -0
- keboola_agent_cli/commands/feature.py +311 -0
- keboola_agent_cli/commands/flow.py +948 -0
- keboola_agent_cli/commands/http_client.py +157 -0
- keboola_agent_cli/commands/init.py +279 -0
- keboola_agent_cli/commands/job.py +661 -0
- keboola_agent_cli/commands/kai.py +301 -0
- keboola_agent_cli/commands/lineage.py +1464 -0
- keboola_agent_cli/commands/org.py +292 -0
- keboola_agent_cli/commands/permissions.py +360 -0
- keboola_agent_cli/commands/project.py +1192 -0
- keboola_agent_cli/commands/repl.py +243 -0
- keboola_agent_cli/commands/schedule.py +340 -0
- keboola_agent_cli/commands/search.py +178 -0
- keboola_agent_cli/commands/semantic_layer.py +939 -0
- keboola_agent_cli/commands/serve.py +272 -0
- keboola_agent_cli/commands/sharing.py +340 -0
- keboola_agent_cli/commands/storage.py +2630 -0
- keboola_agent_cli/commands/stream.py +266 -0
- keboola_agent_cli/commands/sync.py +1277 -0
- keboola_agent_cli/commands/tool.py +206 -0
- keboola_agent_cli/commands/version.py +186 -0
- keboola_agent_cli/commands/workspace.py +635 -0
- keboola_agent_cli/config_store.py +582 -0
- keboola_agent_cli/constants.py +528 -0
- keboola_agent_cli/data_science_client.py +342 -0
- keboola_agent_cli/dev_portal_client.py +323 -0
- keboola_agent_cli/errors.py +248 -0
- keboola_agent_cli/http_base.py +315 -0
- keboola_agent_cli/json_utils.py +126 -0
- keboola_agent_cli/lib.py +536 -0
- keboola_agent_cli/manage_client.py +324 -0
- keboola_agent_cli/metastore_client.py +214 -0
- keboola_agent_cli/models.py +427 -0
- keboola_agent_cli/output.py +1084 -0
- keboola_agent_cli/permissions.py +469 -0
- keboola_agent_cli/py.typed +3 -0
- keboola_agent_cli/result_models.py +271 -0
- keboola_agent_cli/server/__init__.py +34 -0
- keboola_agent_cli/server/agent_runner.py +1289 -0
- keboola_agent_cli/server/agents_store.py +325 -0
- keboola_agent_cli/server/app.py +764 -0
- keboola_agent_cli/server/auth.py +117 -0
- keboola_agent_cli/server/dependencies.py +149 -0
- keboola_agent_cli/server/pricing.py +303 -0
- keboola_agent_cli/server/routers/__init__.py +1 -0
- keboola_agent_cli/server/routers/agents.py +616 -0
- keboola_agent_cli/server/routers/ai_chat.py +129 -0
- keboola_agent_cli/server/routers/branches.py +133 -0
- keboola_agent_cli/server/routers/components.py +48 -0
- keboola_agent_cli/server/routers/configs.py +507 -0
- keboola_agent_cli/server/routers/data_apps.py +384 -0
- keboola_agent_cli/server/routers/dev_portal.py +67 -0
- keboola_agent_cli/server/routers/encrypt.py +35 -0
- keboola_agent_cli/server/routers/feature.py +179 -0
- keboola_agent_cli/server/routers/flows.py +204 -0
- keboola_agent_cli/server/routers/health.py +53 -0
- keboola_agent_cli/server/routers/jobs.py +175 -0
- keboola_agent_cli/server/routers/kai.py +80 -0
- keboola_agent_cli/server/routers/lineage.py +226 -0
- keboola_agent_cli/server/routers/mcp.py +70 -0
- keboola_agent_cli/server/routers/members.py +170 -0
- keboola_agent_cli/server/routers/org.py +96 -0
- keboola_agent_cli/server/routers/projects.py +106 -0
- keboola_agent_cli/server/routers/schedules.py +54 -0
- keboola_agent_cli/server/routers/search.py +30 -0
- keboola_agent_cli/server/routers/semantic_layer.py +650 -0
- keboola_agent_cli/server/routers/sharing.py +86 -0
- keboola_agent_cli/server/routers/storage.py +574 -0
- keboola_agent_cli/server/routers/stream.py +100 -0
- keboola_agent_cli/server/routers/workspaces.py +302 -0
- keboola_agent_cli/server/run_broadcaster.py +329 -0
- keboola_agent_cli/server/sse.py +25 -0
- keboola_agent_cli/services/__init__.py +0 -0
- keboola_agent_cli/services/_encryption.py +217 -0
- keboola_agent_cli/services/_semantic_layer_cascade.py +147 -0
- keboola_agent_cli/services/_semantic_layer_crud.py +382 -0
- keboola_agent_cli/services/_semantic_layer_internals.py +1078 -0
- keboola_agent_cli/services/_semantic_layer_lookup.py +181 -0
- keboola_agent_cli/services/_semantic_layer_reference_data.py +217 -0
- keboola_agent_cli/services/_sync_bindings.py +456 -0
- keboola_agent_cli/services/_sync_branch.py +191 -0
- keboola_agent_cli/services/_sync_bulk.py +228 -0
- keboola_agent_cli/services/_sync_clone.py +163 -0
- keboola_agent_cli/services/_sync_models.py +97 -0
- keboola_agent_cli/services/_sync_push_ops.py +369 -0
- keboola_agent_cli/services/_sync_storage.py +376 -0
- keboola_agent_cli/services/_sync_writeback.py +167 -0
- keboola_agent_cli/services/agent_service.py +458 -0
- keboola_agent_cli/services/base.py +175 -0
- keboola_agent_cli/services/branch_service.py +588 -0
- keboola_agent_cli/services/component_service.py +694 -0
- keboola_agent_cli/services/config_service.py +2099 -0
- keboola_agent_cli/services/data_app_git_service.py +224 -0
- keboola_agent_cli/services/data_app_service.py +2082 -0
- keboola_agent_cli/services/deep_lineage_service.py +1322 -0
- keboola_agent_cli/services/dev_portal_service.py +345 -0
- keboola_agent_cli/services/doctor_service.py +445 -0
- keboola_agent_cli/services/encrypt_service.py +87 -0
- keboola_agent_cli/services/feature_service.py +268 -0
- keboola_agent_cli/services/flow_service.py +769 -0
- keboola_agent_cli/services/flow_validation.py +188 -0
- keboola_agent_cli/services/http_forwarder_service.py +236 -0
- keboola_agent_cli/services/job_idempotency_store.py +285 -0
- keboola_agent_cli/services/job_service.py +797 -0
- keboola_agent_cli/services/kai_service.py +367 -0
- keboola_agent_cli/services/lineage_service.py +274 -0
- keboola_agent_cli/services/mcp_service.py +1498 -0
- keboola_agent_cli/services/mcp_transport.py +259 -0
- keboola_agent_cli/services/member_service.py +593 -0
- keboola_agent_cli/services/org_service.py +619 -0
- keboola_agent_cli/services/project_service.py +947 -0
- keboola_agent_cli/services/repo_validate_service.py +767 -0
- keboola_agent_cli/services/schedule_service.py +731 -0
- keboola_agent_cli/services/search_service.py +331 -0
- keboola_agent_cli/services/semantic_layer_service.py +1497 -0
- keboola_agent_cli/services/sharing_service.py +307 -0
- keboola_agent_cli/services/storage_service.py +2524 -0
- keboola_agent_cli/services/stream_service.py +395 -0
- keboola_agent_cli/services/sync_service.py +2244 -0
- keboola_agent_cli/services/variables_service.py +447 -0
- keboola_agent_cli/services/version_service.py +1038 -0
- keboola_agent_cli/services/workspace_service.py +1103 -0
- keboola_agent_cli/stream_client.py +217 -0
- keboola_agent_cli/sync/__init__.py +1 -0
- keboola_agent_cli/sync/branch_mapping.py +174 -0
- keboola_agent_cli/sync/clone.py +211 -0
- keboola_agent_cli/sync/code_extraction.py +655 -0
- keboola_agent_cli/sync/config_format.py +290 -0
- keboola_agent_cli/sync/diff_engine.py +566 -0
- keboola_agent_cli/sync/git_utils.py +93 -0
- keboola_agent_cli/sync/manifest.py +162 -0
- keboola_agent_cli/sync/naming.py +90 -0
- keboola_agent_cli/sync/secrets.py +62 -0
- keboola_agent_cli/sync/sql_split.py +134 -0
- keboola_cli-0.63.4.dist-info/METADATA +308 -0
- keboola_cli-0.63.4.dist-info/RECORD +306 -0
- keboola_cli-0.63.4.dist-info/WHEEL +4 -0
- keboola_cli-0.63.4.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
"""Semantic-layer endpoints — 18 routes covering 30 CLI subcommands.
|
|
2
|
+
|
|
3
|
+
Mirrors the per-subcommand surface of
|
|
4
|
+
:class:`keboola_agent_cli.services.semantic_layer_service.SemanticLayerService`.
|
|
5
|
+
The 15 ``add.*``/``edit.*``/``remove.*`` CLI leaves collapse into three
|
|
6
|
+
``{kind}``-parameterized routes (``POST/PUT/DELETE /items/{kind}``) for
|
|
7
|
+
RESTful semantics; per-kind Pydantic body validation happens inside the
|
|
8
|
+
handler. This is a deliberate departure from the CONTRIBUTING.md "1:1
|
|
9
|
+
endpoint per command" guideline — the parameterization preserves
|
|
10
|
+
discoverability while keeping the route count manageable.
|
|
11
|
+
|
|
12
|
+
Pattern: Pydantic body models for routes that the CLI flags with multiple
|
|
13
|
+
options, query parameters for read endpoints. ``--yes`` is implicit on
|
|
14
|
+
every REST destructive call (the body / DELETE request IS the confirmation).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import contextlib
|
|
20
|
+
import json as _json
|
|
21
|
+
import tempfile
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Literal
|
|
24
|
+
|
|
25
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
26
|
+
from pydantic import BaseModel, Field, model_validator
|
|
27
|
+
|
|
28
|
+
from ...errors import ErrorCode
|
|
29
|
+
from ..dependencies import ServiceRegistry, get_registry
|
|
30
|
+
|
|
31
|
+
router = APIRouter(prefix="/semantic-layer", tags=["semantic-layer"])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Closed set of remove/edit/add kinds, mirroring the CLI surface.
|
|
35
|
+
ItemKind = Literal["metric", "dataset", "relationship", "constraint", "glossary"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ── Pydantic body models ───────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ModelCreate(BaseModel):
|
|
42
|
+
project: str
|
|
43
|
+
name: str
|
|
44
|
+
description: str = ""
|
|
45
|
+
sql_dialect: str = "Snowflake"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class DiffRequest(BaseModel):
|
|
49
|
+
"""Diff body — exactly one of project_a/file_a and one of project_b/file_b."""
|
|
50
|
+
|
|
51
|
+
project_a: str | None = None
|
|
52
|
+
project_b: str | None = None
|
|
53
|
+
model_a: str | None = None
|
|
54
|
+
model_b: str | None = None
|
|
55
|
+
file_a: dict[str, Any] | None = None
|
|
56
|
+
file_b: dict[str, Any] | None = None
|
|
57
|
+
|
|
58
|
+
@model_validator(mode="before")
|
|
59
|
+
@classmethod
|
|
60
|
+
def _exactly_one_per_side(cls, data: Any) -> Any:
|
|
61
|
+
# Pydantic v2 calls this with the raw input dict in "before" mode.
|
|
62
|
+
if not isinstance(data, dict):
|
|
63
|
+
return data
|
|
64
|
+
left = (data.get("project_a") is not None, data.get("file_a") is not None)
|
|
65
|
+
right = (data.get("project_b") is not None, data.get("file_b") is not None)
|
|
66
|
+
if sum(left) != 1:
|
|
67
|
+
raise ValueError("Exactly one of project_a or file_a must be set.")
|
|
68
|
+
if sum(right) != 1:
|
|
69
|
+
raise ValueError("Exactly one of project_b or file_b must be set.")
|
|
70
|
+
return data
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AddMetric(BaseModel):
|
|
74
|
+
project: str
|
|
75
|
+
model: str | None = None
|
|
76
|
+
name: str
|
|
77
|
+
sql: str
|
|
78
|
+
dataset: str
|
|
79
|
+
description: str = ""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AddDataset(BaseModel):
|
|
83
|
+
project: str
|
|
84
|
+
model: str | None = None
|
|
85
|
+
name: str
|
|
86
|
+
table_id: str
|
|
87
|
+
description: str = ""
|
|
88
|
+
grain: str = ""
|
|
89
|
+
primary_key: list[str] | None = None
|
|
90
|
+
deep_fields: bool = False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class AddRelationship(BaseModel):
|
|
94
|
+
project: str
|
|
95
|
+
model: str | None = None
|
|
96
|
+
name: str
|
|
97
|
+
from_: str = Field(alias="from")
|
|
98
|
+
to: str
|
|
99
|
+
on: str
|
|
100
|
+
type_: str = Field(alias="type", default="left")
|
|
101
|
+
|
|
102
|
+
model_config = {"populate_by_name": True}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class AddConstraint(BaseModel):
|
|
106
|
+
project: str
|
|
107
|
+
model: str | None = None
|
|
108
|
+
name: str
|
|
109
|
+
constraint_type: str
|
|
110
|
+
rule: str
|
|
111
|
+
metrics: list[str]
|
|
112
|
+
severity: str = "warning"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class AddGlossary(BaseModel):
|
|
116
|
+
project: str
|
|
117
|
+
model: str | None = None
|
|
118
|
+
term: str
|
|
119
|
+
definition: str = ""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class EditMetric(BaseModel):
|
|
123
|
+
project: str
|
|
124
|
+
model: str | None = None
|
|
125
|
+
new_name: str | None = None
|
|
126
|
+
new_sql: str | None = None
|
|
127
|
+
new_dataset: str | None = None
|
|
128
|
+
new_description: str | None = None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class EditDataset(BaseModel):
|
|
132
|
+
project: str
|
|
133
|
+
model: str | None = None
|
|
134
|
+
new_name: str | None = None
|
|
135
|
+
new_description: str | None = None
|
|
136
|
+
new_grain: str | None = None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class EditRelationship(BaseModel):
|
|
140
|
+
project: str
|
|
141
|
+
model: str | None = None
|
|
142
|
+
new_name: str | None = None
|
|
143
|
+
new_from: str | None = None
|
|
144
|
+
new_to: str | None = None
|
|
145
|
+
new_on: str | None = None
|
|
146
|
+
new_type: str | None = None
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class EditConstraint(BaseModel):
|
|
150
|
+
project: str
|
|
151
|
+
model: str | None = None
|
|
152
|
+
new_name: str | None = None
|
|
153
|
+
new_rule: str | None = None
|
|
154
|
+
new_constraint_type: str | None = None
|
|
155
|
+
new_severity: str | None = None
|
|
156
|
+
new_metrics: list[str] | None = None
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class EditGlossary(BaseModel):
|
|
160
|
+
project: str
|
|
161
|
+
model: str | None = None
|
|
162
|
+
new_term: str | None = None
|
|
163
|
+
new_definition: str | None = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class ImportRequest(BaseModel):
|
|
167
|
+
project: str
|
|
168
|
+
model: str | None = None
|
|
169
|
+
snapshot: dict[str, Any]
|
|
170
|
+
types: list[str] | None = None
|
|
171
|
+
dry_run: bool = False
|
|
172
|
+
overwrite: bool = False
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class PromoteRequest(BaseModel):
|
|
176
|
+
from_project: str
|
|
177
|
+
to_project: str
|
|
178
|
+
from_model: str | None = None
|
|
179
|
+
to_model: str | None = None
|
|
180
|
+
types: list[str] | None = None
|
|
181
|
+
dry_run: bool = False
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class BuildRequest(BaseModel):
|
|
185
|
+
project: str
|
|
186
|
+
model: str | None = None
|
|
187
|
+
tables: list[str]
|
|
188
|
+
name: str | None = None
|
|
189
|
+
dry_run: bool = False
|
|
190
|
+
keep_on_failure: bool = False
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class TokenEncryptRequest(BaseModel):
|
|
194
|
+
project: str
|
|
195
|
+
component_id: str
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class RefDataSet(BaseModel):
|
|
199
|
+
"""Create-or-replace body for ``PUT /reference-data`` (idempotent on dimension)."""
|
|
200
|
+
|
|
201
|
+
project: str
|
|
202
|
+
model: str | None = None
|
|
203
|
+
dimension: str
|
|
204
|
+
members: list[dict[str, Any]]
|
|
205
|
+
dataset_id: str | None = None
|
|
206
|
+
description: str | None = None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ── Routes (14 declarations, in the order from the plan) ────────────
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@router.get("/models", summary="List semantic-layer models")
|
|
213
|
+
def list_models(
|
|
214
|
+
project: str,
|
|
215
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
216
|
+
) -> dict[str, Any]:
|
|
217
|
+
"""List every semantic-layer model in a project."""
|
|
218
|
+
return registry.semantic_layer.list_models(project)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@router.post("/models", summary="Create a semantic-layer model")
|
|
222
|
+
def create_model(
|
|
223
|
+
body: ModelCreate, registry: ServiceRegistry = Depends(get_registry)
|
|
224
|
+
) -> dict[str, Any]:
|
|
225
|
+
"""Create a semantic-layer model (POST /repository/semantic-model)."""
|
|
226
|
+
return registry.semantic_layer.create_model(
|
|
227
|
+
alias=body.project,
|
|
228
|
+
name=body.name,
|
|
229
|
+
description=body.description,
|
|
230
|
+
sql_dialect=body.sql_dialect,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@router.delete("/models/{model}", summary="Delete a semantic-layer model")
|
|
235
|
+
def delete_model(
|
|
236
|
+
model: str,
|
|
237
|
+
project: str,
|
|
238
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
239
|
+
) -> dict[str, Any]:
|
|
240
|
+
"""Delete a semantic-layer model (--yes implicit on REST)."""
|
|
241
|
+
return registry.semantic_layer.delete_model(alias=project, model_name_or_uuid=model)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@router.get("/show", summary="Show model entities")
|
|
245
|
+
def show(
|
|
246
|
+
project: str,
|
|
247
|
+
model: str | None = None,
|
|
248
|
+
type: str | None = None,
|
|
249
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
250
|
+
) -> dict[str, Any]:
|
|
251
|
+
"""Show all entities (datasets, metrics, ...) for a model."""
|
|
252
|
+
return registry.semantic_layer.show_model(
|
|
253
|
+
alias=project, model_name_or_uuid=model, type_filter=type
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@router.get("/validate", summary="Validate a semantic-layer model")
|
|
258
|
+
def validate(
|
|
259
|
+
project: str,
|
|
260
|
+
model: str | None = None,
|
|
261
|
+
deep: bool = False,
|
|
262
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
263
|
+
) -> dict[str, Any]:
|
|
264
|
+
"""Validate a model — basic checks (always) + Snowflake schema probes (``deep``)."""
|
|
265
|
+
return registry.semantic_layer.validate_model(
|
|
266
|
+
alias=project, model_name_or_uuid=model, deep=deep
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@router.get("/search-context", summary="Search semantic contexts by name pattern")
|
|
271
|
+
def search_context(
|
|
272
|
+
project: str,
|
|
273
|
+
pattern: list[str] = Query(default=["*"]),
|
|
274
|
+
type: str = "all",
|
|
275
|
+
limit: int | None = None,
|
|
276
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
277
|
+
) -> dict[str, Any]:
|
|
278
|
+
"""Project-wide glob search across semantic-layer entities.
|
|
279
|
+
|
|
280
|
+
Mirrors ``kbagent semantic-layer search-context``. See the service-layer
|
|
281
|
+
docstring for matching semantics and the returned envelope shape.
|
|
282
|
+
"""
|
|
283
|
+
return registry.semantic_layer.search_context(
|
|
284
|
+
alias=project, patterns=pattern, type_filter=type, limit=limit
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@router.get("/get-context", summary="Fetch one semantic context by id")
|
|
289
|
+
def get_context(
|
|
290
|
+
project: str,
|
|
291
|
+
context_id: str,
|
|
292
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
293
|
+
) -> dict[str, Any]:
|
|
294
|
+
"""Single-entry fetch by id; probes every type until found."""
|
|
295
|
+
return registry.semantic_layer.get_context(alias=project, context_id=context_id)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@router.get("/export", summary="Export model snapshot")
|
|
299
|
+
def export(
|
|
300
|
+
project: str,
|
|
301
|
+
model: str | None = None,
|
|
302
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
303
|
+
) -> dict[str, Any]:
|
|
304
|
+
"""Export a model + every child entity as an inline JSON snapshot.
|
|
305
|
+
|
|
306
|
+
Unlike the CLI which writes to disk by default, the HTTP route returns
|
|
307
|
+
the snapshot in the response body (no file is written server-side).
|
|
308
|
+
"""
|
|
309
|
+
# output_path=None on the service writes to a default file path; we
|
|
310
|
+
# need to avoid that for REST. We bypass via export_model + pop the
|
|
311
|
+
# transient path. Simpler: route through the service and let the
|
|
312
|
+
# caller ignore the `path` field. Using a tmp-dir export keeps the
|
|
313
|
+
# service contract intact while avoiding pollution of the CWD.
|
|
314
|
+
|
|
315
|
+
with tempfile.TemporaryDirectory(prefix="kbagent-sl-export-") as tmp:
|
|
316
|
+
out = Path(tmp) / "snapshot.json"
|
|
317
|
+
result = registry.semantic_layer.export_model(
|
|
318
|
+
alias=project, model_name_or_uuid=model, output_path=out
|
|
319
|
+
)
|
|
320
|
+
# Strip the now-deleted tmp path from the wire response (the bytes
|
|
321
|
+
# are already captured in the dict).
|
|
322
|
+
result.pop("path", None)
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@router.post("/diff", summary="Diff two semantic-layer snapshots")
|
|
327
|
+
def diff(body: DiffRequest, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
328
|
+
"""Diff two snapshots — project↔project, project↔file, file↔file.
|
|
329
|
+
|
|
330
|
+
File-backed sides carry the snapshot inline in the body (``file_a`` /
|
|
331
|
+
``file_b``). When a file side is set we serialize it to a temp file so
|
|
332
|
+
the existing service contract (``Path``) keeps working.
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
def _write_tmp(payload: dict[str, Any]) -> Path:
|
|
336
|
+
# `delete=False` is required because we close the handle before
|
|
337
|
+
# the service reads the file (the open handle would let us read
|
|
338
|
+
# but the service takes a Path, not a file object). Cleanup is
|
|
339
|
+
# done explicitly in the finally below.
|
|
340
|
+
fd, tmp_name = tempfile.mkstemp(suffix=".json", prefix="kbagent-sl-diff-")
|
|
341
|
+
try:
|
|
342
|
+
with open(fd, "w", encoding="utf-8") as fh:
|
|
343
|
+
fh.write(_json.dumps(payload))
|
|
344
|
+
except Exception:
|
|
345
|
+
with contextlib.suppress(OSError):
|
|
346
|
+
Path(tmp_name).unlink()
|
|
347
|
+
raise
|
|
348
|
+
return Path(tmp_name)
|
|
349
|
+
|
|
350
|
+
file_a_path: Path | None = None
|
|
351
|
+
file_b_path: Path | None = None
|
|
352
|
+
tmps: list[Path] = []
|
|
353
|
+
try:
|
|
354
|
+
if body.file_a is not None:
|
|
355
|
+
file_a_path = _write_tmp(body.file_a)
|
|
356
|
+
tmps.append(file_a_path)
|
|
357
|
+
if body.file_b is not None:
|
|
358
|
+
file_b_path = _write_tmp(body.file_b)
|
|
359
|
+
tmps.append(file_b_path)
|
|
360
|
+
|
|
361
|
+
return registry.semantic_layer.diff(
|
|
362
|
+
project_a=body.project_a,
|
|
363
|
+
project_b=body.project_b,
|
|
364
|
+
model_a=body.model_a,
|
|
365
|
+
model_b=body.model_b,
|
|
366
|
+
file_a=file_a_path,
|
|
367
|
+
file_b=file_b_path,
|
|
368
|
+
)
|
|
369
|
+
finally:
|
|
370
|
+
for p in tmps:
|
|
371
|
+
with contextlib.suppress(OSError):
|
|
372
|
+
p.unlink()
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@router.post("/items/{kind}", summary="Add an entity to a model")
|
|
376
|
+
def add_item(
|
|
377
|
+
kind: ItemKind,
|
|
378
|
+
body: dict[str, Any],
|
|
379
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
380
|
+
) -> dict[str, Any]:
|
|
381
|
+
"""Add an entity to a model. ``kind`` selects the entity type.
|
|
382
|
+
|
|
383
|
+
Per-kind Pydantic body validation is done downstream by binding the
|
|
384
|
+
raw body to the right model before delegating to the service.
|
|
385
|
+
FastAPI rejects unknown ``kind`` values at the framework layer (422)
|
|
386
|
+
via the :data:`ItemKind` ``Literal`` alias.
|
|
387
|
+
"""
|
|
388
|
+
svc = registry.semantic_layer
|
|
389
|
+
if kind == "metric":
|
|
390
|
+
m = AddMetric.model_validate(body)
|
|
391
|
+
return svc.add_metric(
|
|
392
|
+
alias=m.project,
|
|
393
|
+
model_name_or_uuid=m.model,
|
|
394
|
+
name=m.name,
|
|
395
|
+
sql=m.sql,
|
|
396
|
+
dataset=m.dataset,
|
|
397
|
+
description=m.description,
|
|
398
|
+
assume_yes=True,
|
|
399
|
+
is_tty=False,
|
|
400
|
+
)
|
|
401
|
+
if kind == "dataset":
|
|
402
|
+
d = AddDataset.model_validate(body)
|
|
403
|
+
return svc.add_dataset(
|
|
404
|
+
alias=d.project,
|
|
405
|
+
model_name_or_uuid=d.model,
|
|
406
|
+
name=d.name,
|
|
407
|
+
table_id=d.table_id,
|
|
408
|
+
description=d.description,
|
|
409
|
+
grain=d.grain,
|
|
410
|
+
primary_key=d.primary_key,
|
|
411
|
+
deep_fields=d.deep_fields,
|
|
412
|
+
)
|
|
413
|
+
if kind == "relationship":
|
|
414
|
+
r = AddRelationship.model_validate(body)
|
|
415
|
+
return svc.add_relationship(
|
|
416
|
+
alias=r.project,
|
|
417
|
+
model_name_or_uuid=r.model,
|
|
418
|
+
name=r.name,
|
|
419
|
+
from_=r.from_,
|
|
420
|
+
to=r.to,
|
|
421
|
+
on=r.on,
|
|
422
|
+
type_=r.type_,
|
|
423
|
+
)
|
|
424
|
+
if kind == "constraint":
|
|
425
|
+
c = AddConstraint.model_validate(body)
|
|
426
|
+
return svc.add_constraint(
|
|
427
|
+
alias=c.project,
|
|
428
|
+
model_name_or_uuid=c.model,
|
|
429
|
+
name=c.name,
|
|
430
|
+
constraint_type=c.constraint_type,
|
|
431
|
+
rule=c.rule,
|
|
432
|
+
metrics=c.metrics,
|
|
433
|
+
severity=c.severity,
|
|
434
|
+
)
|
|
435
|
+
if kind == "glossary":
|
|
436
|
+
g = AddGlossary.model_validate(body)
|
|
437
|
+
return svc.add_glossary(
|
|
438
|
+
alias=g.project,
|
|
439
|
+
model_name_or_uuid=g.model,
|
|
440
|
+
term=g.term,
|
|
441
|
+
definition=g.definition,
|
|
442
|
+
)
|
|
443
|
+
raise HTTPException(
|
|
444
|
+
status_code=404,
|
|
445
|
+
detail=(
|
|
446
|
+
f"Unknown item kind {kind!r}. "
|
|
447
|
+
f"Must be one of: metric, dataset, relationship, constraint, glossary."
|
|
448
|
+
),
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
@router.put("/items/{kind}/{name}", summary="Edit a model entity")
|
|
453
|
+
def edit_item(
|
|
454
|
+
kind: ItemKind,
|
|
455
|
+
name: str,
|
|
456
|
+
body: dict[str, Any],
|
|
457
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
458
|
+
) -> dict[str, Any]:
|
|
459
|
+
"""Edit an entity. ``name`` is the current identifier; new-* fields live in the body.
|
|
460
|
+
|
|
461
|
+
For ``kind="glossary"``, ``name`` is the current ``term`` (not a stored
|
|
462
|
+
field called ``name``). FastAPI rejects unknown ``kind`` values at the
|
|
463
|
+
framework layer (422) via the :data:`ItemKind` ``Literal`` alias.
|
|
464
|
+
"""
|
|
465
|
+
svc = registry.semantic_layer
|
|
466
|
+
if kind == "metric":
|
|
467
|
+
m = EditMetric.model_validate(body)
|
|
468
|
+
return svc.edit_metric(
|
|
469
|
+
alias=m.project,
|
|
470
|
+
model_name_or_uuid=m.model,
|
|
471
|
+
current_name=name,
|
|
472
|
+
new_name=m.new_name,
|
|
473
|
+
new_sql=m.new_sql,
|
|
474
|
+
new_dataset=m.new_dataset,
|
|
475
|
+
new_description=m.new_description,
|
|
476
|
+
assume_yes=True,
|
|
477
|
+
is_tty=False,
|
|
478
|
+
)
|
|
479
|
+
if kind == "dataset":
|
|
480
|
+
d = EditDataset.model_validate(body)
|
|
481
|
+
return svc.edit_dataset(
|
|
482
|
+
alias=d.project,
|
|
483
|
+
model_name_or_uuid=d.model,
|
|
484
|
+
current_name=name,
|
|
485
|
+
new_name=d.new_name,
|
|
486
|
+
new_description=d.new_description,
|
|
487
|
+
new_grain=d.new_grain,
|
|
488
|
+
)
|
|
489
|
+
if kind == "relationship":
|
|
490
|
+
r = EditRelationship.model_validate(body)
|
|
491
|
+
return svc.edit_relationship(
|
|
492
|
+
alias=r.project,
|
|
493
|
+
model_name_or_uuid=r.model,
|
|
494
|
+
current_name=name,
|
|
495
|
+
new_name=r.new_name,
|
|
496
|
+
new_from=r.new_from,
|
|
497
|
+
new_to=r.new_to,
|
|
498
|
+
new_on=r.new_on,
|
|
499
|
+
new_type=r.new_type,
|
|
500
|
+
)
|
|
501
|
+
if kind == "constraint":
|
|
502
|
+
c = EditConstraint.model_validate(body)
|
|
503
|
+
return svc.edit_constraint(
|
|
504
|
+
alias=c.project,
|
|
505
|
+
model_name_or_uuid=c.model,
|
|
506
|
+
current_name=name,
|
|
507
|
+
new_name=c.new_name,
|
|
508
|
+
new_rule=c.new_rule,
|
|
509
|
+
new_constraint_type=c.new_constraint_type,
|
|
510
|
+
new_severity=c.new_severity,
|
|
511
|
+
new_metrics=c.new_metrics,
|
|
512
|
+
)
|
|
513
|
+
if kind == "glossary":
|
|
514
|
+
g = EditGlossary.model_validate(body)
|
|
515
|
+
return svc.edit_glossary(
|
|
516
|
+
alias=g.project,
|
|
517
|
+
model_name_or_uuid=g.model,
|
|
518
|
+
current_term=name,
|
|
519
|
+
new_term=g.new_term,
|
|
520
|
+
new_definition=g.new_definition,
|
|
521
|
+
)
|
|
522
|
+
raise HTTPException(
|
|
523
|
+
status_code=404,
|
|
524
|
+
detail=(
|
|
525
|
+
f"Unknown item kind {kind!r}. "
|
|
526
|
+
f"Must be one of: metric, dataset, relationship, constraint, glossary."
|
|
527
|
+
),
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@router.delete("/items/{kind}/{name}", summary="Remove a model entity")
|
|
532
|
+
def remove_item(
|
|
533
|
+
kind: ItemKind,
|
|
534
|
+
name: str,
|
|
535
|
+
project: str,
|
|
536
|
+
model: str | None = None,
|
|
537
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
538
|
+
) -> dict[str, Any]:
|
|
539
|
+
"""Remove a child entity (``--yes`` implicit on REST).
|
|
540
|
+
|
|
541
|
+
For ``kind="glossary"``, ``name`` is the term to remove. FastAPI
|
|
542
|
+
rejects unknown ``kind`` values at the framework layer (422) via the
|
|
543
|
+
:data:`ItemKind` ``Literal`` alias.
|
|
544
|
+
"""
|
|
545
|
+
return registry.semantic_layer.remove_item(
|
|
546
|
+
alias=project, model_name_or_uuid=model, kind=kind, name=name
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
@router.post("/import", summary="Import a snapshot into a project")
|
|
551
|
+
def import_snapshot(
|
|
552
|
+
body: ImportRequest, registry: ServiceRegistry = Depends(get_registry)
|
|
553
|
+
) -> dict[str, Any]:
|
|
554
|
+
"""Replay an inline snapshot into a project. Default: skip on conflict."""
|
|
555
|
+
return registry.semantic_layer.import_snapshot_from_dict(
|
|
556
|
+
body.project,
|
|
557
|
+
snapshot=body.snapshot,
|
|
558
|
+
model_name_or_uuid=body.model,
|
|
559
|
+
types=body.types,
|
|
560
|
+
dry_run=body.dry_run,
|
|
561
|
+
overwrite=body.overwrite,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
@router.post("/promote", summary="Promote a model between projects")
|
|
566
|
+
def promote(
|
|
567
|
+
body: PromoteRequest, registry: ServiceRegistry = Depends(get_registry)
|
|
568
|
+
) -> dict[str, Any]:
|
|
569
|
+
"""Promote a model from one project to another (additive + overwrite)."""
|
|
570
|
+
return registry.semantic_layer.promote_model(
|
|
571
|
+
from_project=body.from_project,
|
|
572
|
+
to_project=body.to_project,
|
|
573
|
+
from_model=body.from_model,
|
|
574
|
+
to_model=body.to_model,
|
|
575
|
+
types=body.types,
|
|
576
|
+
dry_run=body.dry_run,
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
@router.post("/build", summary="Build a model from tables")
|
|
581
|
+
def build(body: BuildRequest, registry: ServiceRegistry = Depends(get_registry)) -> dict[str, Any]:
|
|
582
|
+
"""Heuristic greenfield builder — synthesize a model from a list of tables."""
|
|
583
|
+
return registry.semantic_layer.build_model(
|
|
584
|
+
alias=body.project,
|
|
585
|
+
table_ids=body.tables,
|
|
586
|
+
model_name=body.name,
|
|
587
|
+
model_name_or_uuid=body.model,
|
|
588
|
+
dry_run=body.dry_run,
|
|
589
|
+
keep_on_failure=body.keep_on_failure,
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
@router.post("/token/encrypt", summary="Encrypt storage token for transformation")
|
|
594
|
+
def token_encrypt(
|
|
595
|
+
body: TokenEncryptRequest, registry: ServiceRegistry = Depends(get_registry)
|
|
596
|
+
) -> dict[str, Any]:
|
|
597
|
+
"""Encrypt the project's storage token for transformation ``user_properties``."""
|
|
598
|
+
return registry.semantic_layer.encrypt_token(alias=body.project, component_id=body.component_id)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
# ── reference-data (dimension-member records, e.g. a Chart of Accounts) ──
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
@router.get("/reference-data", summary="List reference-data records")
|
|
605
|
+
def list_reference_data(
|
|
606
|
+
project: str,
|
|
607
|
+
model: str | None = None,
|
|
608
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
609
|
+
) -> dict[str, Any]:
|
|
610
|
+
"""List dimension-member records (summaries; use the by-id route for members)."""
|
|
611
|
+
return registry.semantic_layer.list_reference_data(alias=project, model_name_or_uuid=model)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
@router.get("/reference-data/{record_id}", summary="Get one reference-data record")
|
|
615
|
+
def get_reference_data(
|
|
616
|
+
record_id: str,
|
|
617
|
+
project: str,
|
|
618
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
619
|
+
) -> dict[str, Any]:
|
|
620
|
+
"""Fetch one record (all members) by UUID."""
|
|
621
|
+
return registry.semantic_layer.get_reference_data(alias=project, record_id=record_id)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
@router.put("/reference-data", summary="Create or replace a reference-data record")
|
|
625
|
+
def set_reference_data(
|
|
626
|
+
body: RefDataSet, registry: ServiceRegistry = Depends(get_registry)
|
|
627
|
+
) -> dict[str, Any]:
|
|
628
|
+
"""Create or replace (by model + dimension) a record. Idempotent; PUT semantics."""
|
|
629
|
+
return registry.semantic_layer.set_reference_data(
|
|
630
|
+
alias=body.project,
|
|
631
|
+
model_name_or_uuid=body.model,
|
|
632
|
+
dimension=body.dimension,
|
|
633
|
+
members=body.members,
|
|
634
|
+
dataset_id=body.dataset_id,
|
|
635
|
+
description=body.description,
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
@router.delete("/reference-data/{record_id}", summary="Delete a reference-data record")
|
|
640
|
+
def delete_reference_data(
|
|
641
|
+
record_id: str,
|
|
642
|
+
project: str,
|
|
643
|
+
registry: ServiceRegistry = Depends(get_registry),
|
|
644
|
+
) -> dict[str, Any]:
|
|
645
|
+
"""Delete a record by UUID (``--yes`` implicit on REST; server-side soft-delete)."""
|
|
646
|
+
return registry.semantic_layer.delete_reference_data(alias=project, record_id=record_id)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
# Re-export the closed set of kinds for tests / docs.
|
|
650
|
+
__all__ = ["ErrorCode", "ItemKind", "router"]
|