databay-cli 0.1.0__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.
- databay/__init__.py +5 -0
- databay/__main__.py +7 -0
- databay/agent_skill.py +184 -0
- databay/api.py +626 -0
- databay/cli/__init__.py +0 -0
- databay/cli/auth.py +377 -0
- databay/cli/build.py +89 -0
- databay/cli/commands/__init__.py +0 -0
- databay/cli/commands/alerting_webhook.py +19 -0
- databay/cli/commands/alerting_webhook_tag.py +14 -0
- databay/cli/commands/alerting_webhook_type.py +14 -0
- databay/cli/commands/alerting_webhook_undelivered.py +15 -0
- databay/cli/commands/announcement.py +17 -0
- databay/cli/commands/api_key.py +18 -0
- databay/cli/commands/api_key_project.py +16 -0
- databay/cli/commands/backfill_reason_code.py +14 -0
- databay/cli/commands/backfill_run.py +17 -0
- databay/cli/commands/canvas.py +16 -0
- databay/cli/commands/canvas_graph.py +15 -0
- databay/cli/commands/canvas_table_check.py +15 -0
- databay/cli/commands/dashboards.py +21 -0
- databay/cli/commands/dashboards_card.py +20 -0
- databay/cli/commands/dashboards_dataset.py +14 -0
- databay/cli/commands/dashboards_folder.py +16 -0
- databay/cli/commands/dashboards_home.py +17 -0
- databay/cli/commands/dashboards_placement.py +16 -0
- databay/cli/commands/db.py +19 -0
- databay/cli/commands/db_column.py +14 -0
- databay/cli/commands/docs_article.py +16 -0
- databay/cli/commands/dq_result.py +14 -0
- databay/cli/commands/dq_rule.py +16 -0
- databay/cli/commands/feature.py +14 -0
- databay/cli/commands/google_ads_source.py +14 -0
- databay/cli/commands/google_analytics_source.py +14 -0
- databay/cli/commands/instagram_account.py +14 -0
- databay/cli/commands/instagram_source.py +14 -0
- databay/cli/commands/linkedin_account.py +14 -0
- databay/cli/commands/linkedin_source.py +14 -0
- databay/cli/commands/me.py +14 -0
- databay/cli/commands/meta_ads_source.py +14 -0
- databay/cli/commands/metric_models.py +26 -0
- databay/cli/commands/metrics.py +14 -0
- databay/cli/commands/models.py +16 -0
- databay/cli/commands/nlq.py +15 -0
- databay/cli/commands/notebook_credential.py +16 -0
- databay/cli/commands/org.py +14 -0
- databay/cli/commands/org_member.py +17 -0
- databay/cli/commands/org_offboarding.py +14 -0
- databay/cli/commands/pipe.py +26 -0
- databay/cli/commands/pipe_backfill_run.py +14 -0
- databay/cli/commands/pipe_config_option.py +14 -0
- databay/cli/commands/pipe_destination.py +16 -0
- databay/cli/commands/pipe_leftover.py +16 -0
- databay/cli/commands/pipe_sink_type.py +14 -0
- databay/cli/commands/pipe_source_type.py +14 -0
- databay/cli/commands/pipe_status_bucket.py +14 -0
- databay/cli/commands/pipe_stuck_delete.py +14 -0
- databay/cli/commands/plan.py +14 -0
- databay/cli/commands/project.py +21 -0
- databay/cli/commands/project_budget.py +15 -0
- databay/cli/commands/project_member.py +18 -0
- databay/cli/commands/project_privilege.py +16 -0
- databay/cli/commands/project_share.py +16 -0
- databay/cli/commands/provision.py +14 -0
- databay/cli/commands/provisioning.py +14 -0
- databay/cli/commands/registry.py +14 -0
- databay/cli/commands/resource_home.py +14 -0
- databay/cli/commands/schema_change.py +18 -0
- databay/cli/commands/schema_history.py +17 -0
- databay/cli/commands/schema_version.py +16 -0
- databay/cli/commands/shopify_store.py +14 -0
- databay/cli/commands/sink_data_type.py +14 -0
- databay/cli/commands/source.py +24 -0
- databay/cli/commands/source_catalog.py +15 -0
- databay/cli/commands/source_check_run.py +17 -0
- databay/cli/commands/source_hidden_type.py +14 -0
- databay/cli/commands/source_name.py +14 -0
- databay/cli/commands/source_pipe.py +14 -0
- databay/cli/commands/source_publication.py +16 -0
- databay/cli/commands/source_readiness_override.py +15 -0
- databay/cli/commands/source_setup_choice.py +16 -0
- databay/cli/commands/source_stream.py +15 -0
- databay/cli/commands/source_table.py +14 -0
- databay/cli/commands/ssbi_job.py +18 -0
- databay/cli/commands/transform_type.py +14 -0
- databay/cli/commands/usage.py +14 -0
- databay/cli/commands/warehouse.py +21 -0
- databay/cli/commands/warehouse_access_assignment.py +16 -0
- databay/cli/commands/warehouse_access_role.py +22 -0
- databay/cli/commands/warehouse_access_settings.py +17 -0
- databay/cli/commands/warehouse_login.py +20 -0
- databay/cli/commands/warehouse_principal.py +17 -0
- databay/cli/commands/warehouse_service_account.py +17 -0
- databay/cli/commands/warehouse_service_account_credential.py +18 -0
- databay/cli/common.py +93 -0
- databay/cli/console.py +121 -0
- databay/cli/flags.py +183 -0
- databay/cli/handwritten.py +22 -0
- databay/cli/main.py +198 -0
- databay/cli/metrics.py +171 -0
- databay/cli/org.py +193 -0
- databay/cli/output.py +134 -0
- databay/cli/pipe_wait.py +357 -0
- databay/cli/profile.py +230 -0
- databay/cli/runner.py +432 -0
- databay/cli/skill.py +64 -0
- databay/cli/sql.py +84 -0
- databay/compat.py +161 -0
- databay/credentials.py +217 -0
- databay/error_catalog.py +226 -0
- databay/error_codes.py +3265 -0
- databay/errors.py +222 -0
- databay/feature_header.py +40 -0
- databay/invoke.py +225 -0
- databay/locks.py +41 -0
- databay/login_flow.py +245 -0
- databay/model_base.py +40 -0
- databay/models/__init__.py +0 -0
- databay/models/alerting_webhook.py +46 -0
- databay/models/alerting_webhook_tag.py +11 -0
- databay/models/alerting_webhook_type.py +11 -0
- databay/models/alerting_webhook_undelivered.py +23 -0
- databay/models/announcement.py +37 -0
- databay/models/api_key.py +81 -0
- databay/models/api_key_project.py +29 -0
- databay/models/backfill_reason_code.py +11 -0
- databay/models/backfill_run.py +41 -0
- databay/models/canvas.py +68 -0
- databay/models/canvas_graph.py +86 -0
- databay/models/canvas_table_check.py +181 -0
- databay/models/dashboards.py +79 -0
- databay/models/dashboards_card.py +37 -0
- databay/models/dashboards_dataset.py +27 -0
- databay/models/dashboards_folder.py +18 -0
- databay/models/dashboards_home.py +28 -0
- databay/models/db.py +34 -0
- databay/models/db_column.py +11 -0
- databay/models/docs_article.py +59 -0
- databay/models/dq_result.py +27 -0
- databay/models/dq_rule.py +43 -0
- databay/models/feature.py +15 -0
- databay/models/google_ads_source.py +11 -0
- databay/models/google_analytics_source.py +11 -0
- databay/models/instagram_source.py +11 -0
- databay/models/linkedin_source.py +11 -0
- databay/models/me.py +71 -0
- databay/models/meta_ads_source.py +11 -0
- databay/models/metric_models.py +274 -0
- databay/models/metrics.py +158 -0
- databay/models/models.py +73 -0
- databay/models/nlq.py +92 -0
- databay/models/notebook_credential.py +51 -0
- databay/models/org.py +44 -0
- databay/models/org_member.py +123 -0
- databay/models/org_offboarding.py +36 -0
- databay/models/pipe.py +340 -0
- databay/models/pipe_config_option.py +25 -0
- databay/models/pipe_destination.py +128 -0
- databay/models/pipe_leftover.py +51 -0
- databay/models/pipe_sink_type.py +11 -0
- databay/models/pipe_source_type.py +11 -0
- databay/models/pipe_status_bucket.py +13 -0
- databay/models/pipe_stuck_delete.py +25 -0
- databay/models/plan.py +37 -0
- databay/models/project.py +154 -0
- databay/models/project_budget.py +22 -0
- databay/models/project_member.py +67 -0
- databay/models/project_privilege.py +21 -0
- databay/models/project_share.py +76 -0
- databay/models/provision.py +12 -0
- databay/models/provisioning.py +20 -0
- databay/models/registry.py +18 -0
- databay/models/resource_home.py +35 -0
- databay/models/schema_change.py +96 -0
- databay/models/schema_history.py +32 -0
- databay/models/schema_version.py +23 -0
- databay/models/shared.py +875 -0
- databay/models/shopify_store.py +19 -0
- databay/models/sink_data_type.py +11 -0
- databay/models/source.py +263 -0
- databay/models/source_catalog.py +82 -0
- databay/models/source_check_run.py +63 -0
- databay/models/source_hidden_type.py +13 -0
- databay/models/source_name.py +18 -0
- databay/models/source_pipe.py +16 -0
- databay/models/source_publication.py +30 -0
- databay/models/source_readiness_override.py +13 -0
- databay/models/source_setup_choice.py +33 -0
- databay/models/source_stream.py +65 -0
- databay/models/source_table.py +27 -0
- databay/models/sql.py +31 -0
- databay/models/ssbi_job.py +97 -0
- databay/models/transform_type.py +40 -0
- databay/models/usage.py +112 -0
- databay/models/warehouse.py +38 -0
- databay/models/warehouse_access_assignment.py +70 -0
- databay/models/warehouse_access_role.py +148 -0
- databay/models/warehouse_access_settings.py +35 -0
- databay/models/warehouse_login.py +20 -0
- databay/models/warehouse_principal.py +49 -0
- databay/models/warehouse_service_account.py +35 -0
- databay/models/warehouse_service_account_credential.py +11 -0
- databay/oidc.py +205 -0
- databay/operation.py +191 -0
- databay/operations/__init__.py +0 -0
- databay/operations/alerting_webhook.py +317 -0
- databay/operations/alerting_webhook_tag.py +35 -0
- databay/operations/alerting_webhook_type.py +35 -0
- databay/operations/alerting_webhook_undelivered.py +74 -0
- databay/operations/announcement.py +135 -0
- databay/operations/api_key.py +198 -0
- databay/operations/api_key_project.py +130 -0
- databay/operations/backfill_reason_code.py +35 -0
- databay/operations/backfill_run.py +245 -0
- databay/operations/canvas.py +136 -0
- databay/operations/canvas_graph.py +102 -0
- databay/operations/canvas_table_check.py +101 -0
- databay/operations/dashboards.py +441 -0
- databay/operations/dashboards_card.py +406 -0
- databay/operations/dashboards_dataset.py +50 -0
- databay/operations/dashboards_folder.py +146 -0
- databay/operations/dashboards_home.py +176 -0
- databay/operations/dashboards_placement.py +152 -0
- databay/operations/db.py +337 -0
- databay/operations/db_column.py +57 -0
- databay/operations/docs_article.py +101 -0
- databay/operations/dq_result.py +43 -0
- databay/operations/dq_rule.py +155 -0
- databay/operations/feature.py +35 -0
- databay/operations/google_ads_source.py +40 -0
- databay/operations/google_analytics_source.py +40 -0
- databay/operations/instagram_account.py +47 -0
- databay/operations/instagram_source.py +40 -0
- databay/operations/linkedin_account.py +47 -0
- databay/operations/linkedin_source.py +40 -0
- databay/operations/me.py +57 -0
- databay/operations/meta_ads_source.py +40 -0
- databay/operations/metric_models.py +612 -0
- databay/operations/metrics.py +218 -0
- databay/operations/models.py +123 -0
- databay/operations/nlq.py +100 -0
- databay/operations/notebook_credential.py +95 -0
- databay/operations/org.py +79 -0
- databay/operations/org_member.py +192 -0
- databay/operations/org_offboarding.py +43 -0
- databay/operations/pipe.py +882 -0
- databay/operations/pipe_backfill_run.py +72 -0
- databay/operations/pipe_config_option.py +50 -0
- databay/operations/pipe_destination.py +178 -0
- databay/operations/pipe_leftover.py +110 -0
- databay/operations/pipe_sink_type.py +35 -0
- databay/operations/pipe_source_type.py +35 -0
- databay/operations/pipe_status_bucket.py +35 -0
- databay/operations/pipe_stuck_delete.py +52 -0
- databay/operations/plan.py +35 -0
- databay/operations/project.py +346 -0
- databay/operations/project_budget.py +135 -0
- databay/operations/project_member.py +226 -0
- databay/operations/project_privilege.py +143 -0
- databay/operations/project_share.py +160 -0
- databay/operations/provision.py +35 -0
- databay/operations/provisioning.py +35 -0
- databay/operations/registry.py +35 -0
- databay/operations/resource_home.py +80 -0
- databay/operations/schema_change.py +222 -0
- databay/operations/schema_history.py +136 -0
- databay/operations/schema_version.py +115 -0
- databay/operations/shopify_store.py +40 -0
- databay/operations/sink_data_type.py +43 -0
- databay/operations/source.py +1207 -0
- databay/operations/source_catalog.py +135 -0
- databay/operations/source_check_run.py +242 -0
- databay/operations/source_hidden_type.py +59 -0
- databay/operations/source_name.py +69 -0
- databay/operations/source_pipe.py +57 -0
- databay/operations/source_publication.py +139 -0
- databay/operations/source_readiness_override.py +96 -0
- databay/operations/source_setup_choice.py +161 -0
- databay/operations/source_stream.py +99 -0
- databay/operations/source_table.py +59 -0
- databay/operations/sql.py +54 -0
- databay/operations/ssbi_job.py +242 -0
- databay/operations/transform_type.py +35 -0
- databay/operations/usage.py +35 -0
- databay/operations/warehouse.py +343 -0
- databay/operations/warehouse_access_assignment.py +179 -0
- databay/operations/warehouse_access_role.py +473 -0
- databay/operations/warehouse_access_settings.py +172 -0
- databay/operations/warehouse_login.py +308 -0
- databay/operations/warehouse_principal.py +174 -0
- databay/operations/warehouse_service_account.py +206 -0
- databay/operations/warehouse_service_account_credential.py +285 -0
- databay/paths.py +32 -0
- databay/platform_check.py +20 -0
- databay/profiles.py +181 -0
- databay/redact.py +22 -0
- databay/reference.py +218 -0
- databay/resource.py +167 -0
- databay/resources/__init__.py +0 -0
- databay/resources/alerting_webhook.py +84 -0
- databay/resources/alerting_webhook_tag.py +21 -0
- databay/resources/alerting_webhook_type.py +21 -0
- databay/resources/alerting_webhook_undelivered.py +35 -0
- databay/resources/announcement.py +45 -0
- databay/resources/api_key.py +56 -0
- databay/resources/api_key_project.py +47 -0
- databay/resources/backfill_reason_code.py +21 -0
- databay/resources/backfill_run.py +78 -0
- databay/resources/canvas.py +43 -0
- databay/resources/canvas_graph.py +38 -0
- databay/resources/canvas_table_check.py +38 -0
- databay/resources/dashboards.py +105 -0
- databay/resources/dashboards_card.py +97 -0
- databay/resources/dashboards_dataset.py +27 -0
- databay/resources/dashboards_folder.py +47 -0
- databay/resources/dashboards_home.py +59 -0
- databay/resources/dashboards_placement.py +47 -0
- databay/resources/db.py +87 -0
- databay/resources/db_column.py +28 -0
- databay/resources/docs_article.py +37 -0
- databay/resources/dq_result.py +25 -0
- databay/resources/dq_rule.py +48 -0
- databay/resources/feature.py +21 -0
- databay/resources/google_ads_source.py +25 -0
- databay/resources/google_analytics_source.py +25 -0
- databay/resources/instagram_account.py +27 -0
- databay/resources/instagram_source.py +25 -0
- databay/resources/linkedin_account.py +27 -0
- databay/resources/linkedin_source.py +25 -0
- databay/resources/me.py +27 -0
- databay/resources/meta_ads_source.py +25 -0
- databay/resources/metric_models.py +154 -0
- databay/resources/metrics.py +57 -0
- databay/resources/models.py +42 -0
- databay/resources/nlq.py +34 -0
- databay/resources/notebook_credential.py +41 -0
- databay/resources/org.py +29 -0
- databay/resources/org_member.py +53 -0
- databay/resources/org_offboarding.py +25 -0
- databay/resources/pipe.py +208 -0
- databay/resources/pipe_backfill_run.py +28 -0
- databay/resources/pipe_config_option.py +26 -0
- databay/resources/pipe_destination.py +49 -0
- databay/resources/pipe_leftover.py +42 -0
- databay/resources/pipe_sink_type.py +21 -0
- databay/resources/pipe_source_type.py +21 -0
- databay/resources/pipe_status_bucket.py +21 -0
- databay/resources/pipe_stuck_delete.py +27 -0
- databay/resources/plan.py +21 -0
- databay/resources/project.py +92 -0
- databay/resources/project_budget.py +38 -0
- databay/resources/project_member.py +68 -0
- databay/resources/project_privilege.py +48 -0
- databay/resources/project_share.py +47 -0
- databay/resources/provision.py +21 -0
- databay/resources/provisioning.py +21 -0
- databay/resources/registry.py +21 -0
- databay/resources/resource_home.py +29 -0
- databay/resources/schema_change.py +62 -0
- databay/resources/schema_history.py +52 -0
- databay/resources/schema_version.py +43 -0
- databay/resources/shopify_store.py +25 -0
- databay/resources/sink_data_type.py +25 -0
- databay/resources/source.py +132 -0
- databay/resources/source_catalog.py +34 -0
- databay/resources/source_check_run.py +67 -0
- databay/resources/source_hidden_type.py +25 -0
- databay/resources/source_name.py +27 -0
- databay/resources/source_pipe.py +28 -0
- databay/resources/source_publication.py +47 -0
- databay/resources/source_readiness_override.py +37 -0
- databay/resources/source_setup_choice.py +50 -0
- databay/resources/source_stream.py +38 -0
- databay/resources/source_table.py +27 -0
- databay/resources/sql.py +27 -0
- databay/resources/ssbi_job.py +69 -0
- databay/resources/transform_type.py +21 -0
- databay/resources/usage.py +21 -0
- databay/resources/warehouse.py +136 -0
- databay/resources/warehouse_access_assignment.py +51 -0
- databay/resources/warehouse_access_role.py +118 -0
- databay/resources/warehouse_access_settings.py +56 -0
- databay/resources/warehouse_login.py +81 -0
- databay/resources/warehouse_principal.py +51 -0
- databay/resources/warehouse_service_account.py +61 -0
- databay/resources/warehouse_service_account_credential.py +78 -0
- databay/sdk.py +77 -0
- databay/settings.py +90 -0
- databay/skill/SKILL.md +357 -0
- databay/skill/references/commands.md +7025 -0
- databay/skill/references/errors.md +727 -0
- databay/spec.py +362 -0
- databay/stored_token.py +134 -0
- databay/tokens.py +108 -0
- databay/transport.py +357 -0
- databay/update_check.py +151 -0
- databay/version.py +6 -0
- databay/waits.py +330 -0
- databay_cli-0.1.0.dist-info/METADATA +42 -0
- databay_cli-0.1.0.dist-info/RECORD +403 -0
- databay_cli-0.1.0.dist-info/WHEEL +4 -0
- databay_cli-0.1.0.dist-info/entry_points.txt +2 -0
- databay_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
databay/login_flow.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Signing in: the browser and device flows, and keeping what they return.
|
|
3
|
+
|
|
4
|
+
- Browser: authorization code with PKCE; Keycloak sends the browser back to
|
|
5
|
+
``http://127.0.0.1:<random port>/callback``, served here for one request.
|
|
6
|
+
- Device: the device authorization grant, also with PKCE (the ``databay-cli`` client requires it on
|
|
7
|
+
every flow). Chosen by ``--device``, and automatically over SSH, on Linux without a display, or
|
|
8
|
+
when Python finds no browser: the loopback callback would not reach the user's real browser.
|
|
9
|
+
- API key: the client-credentials grant with a key's client id and secret.
|
|
10
|
+
|
|
11
|
+
Both person flows ask for ``offline_access`` and fail ``offline_access_not_granted`` without it.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import secrets
|
|
17
|
+
import time
|
|
18
|
+
import webbrowser
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
21
|
+
from typing import TYPE_CHECKING, Any
|
|
22
|
+
from urllib.parse import parse_qs, urlencode, urlparse
|
|
23
|
+
|
|
24
|
+
from databay import oidc
|
|
25
|
+
from databay.credentials import Credential
|
|
26
|
+
from databay.errors import DatabayError, DatabayProfileError
|
|
27
|
+
from databay.settings import SETTINGS
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from collections.abc import Callable
|
|
31
|
+
|
|
32
|
+
import httpx
|
|
33
|
+
|
|
34
|
+
from databay.cli.console import Console
|
|
35
|
+
from databay.compat import ServerInfo
|
|
36
|
+
|
|
37
|
+
#: The interval RFC 8628 (section 3.2) sets when the device answer gives none, and the step ``slow_down`` adds.
|
|
38
|
+
DEVICE_POLL_DEFAULT_S = 5
|
|
39
|
+
DEVICE_SLOW_DOWN_S = 5
|
|
40
|
+
CALLBACK_READ_TIMEOUT_S = 5
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def device_reason(environ: dict[str, str], platform: str, has_browser: Callable[[], bool]) -> str | None:
|
|
44
|
+
"""Why the device flow is used although ``--device`` was not passed; ``None`` keeps the browser flow."""
|
|
45
|
+
if environ.get("SSH_CONNECTION") or environ.get("SSH_TTY"):
|
|
46
|
+
return "this is an SSH session"
|
|
47
|
+
if platform.startswith("linux") and not (environ.get("DISPLAY") or environ.get("WAYLAND_DISPLAY")):
|
|
48
|
+
return "there is no display"
|
|
49
|
+
if not has_browser():
|
|
50
|
+
return "no local browser"
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def local_browser() -> bool:
|
|
55
|
+
try:
|
|
56
|
+
webbrowser.get()
|
|
57
|
+
except webbrowser.Error:
|
|
58
|
+
return False
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _refused(exc: oidc.OAuthRefusal) -> DatabayError:
|
|
63
|
+
if exc.error in {"expired_token", "access_denied"}:
|
|
64
|
+
exc.code = exc.error
|
|
65
|
+
return exc
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class SignIn:
|
|
70
|
+
"""What a flow got: the provider's endpoints, the grant and its subject."""
|
|
71
|
+
|
|
72
|
+
endpoints: oidc.Endpoints
|
|
73
|
+
issued: oidc.Grant
|
|
74
|
+
sub: str
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _checked(endpoints: oidc.Endpoints, issued: oidc.Grant, *, offline: bool) -> SignIn:
|
|
78
|
+
if offline and (oidc.OFFLINE_SCOPE not in issued.scope or issued.refresh_token is None):
|
|
79
|
+
raise DatabayProfileError(
|
|
80
|
+
"Keycloak signed you in without offline_access, so the CLI would lose the session within a day",
|
|
81
|
+
code="offline_access_not_granted",
|
|
82
|
+
details={"granted_scope": " ".join(issued.scope)},
|
|
83
|
+
)
|
|
84
|
+
return SignIn(endpoints, issued, oidc.claims(issued.access_token)["sub"])
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class _Callback(BaseHTTPRequestHandler):
|
|
88
|
+
"""Answers the browser's one return to the loopback; ``server.query`` keeps its parameters."""
|
|
89
|
+
|
|
90
|
+
#: Seconds a connection may stay silent: a browser's idle preconnect socket must not hold the
|
|
91
|
+
#: one-request-at-a-time server while the real redirect waits behind it.
|
|
92
|
+
timeout = CALLBACK_READ_TIMEOUT_S
|
|
93
|
+
|
|
94
|
+
def do_GET(self) -> None:
|
|
95
|
+
url = urlparse(self.path)
|
|
96
|
+
if url.path != "/callback":
|
|
97
|
+
self.send_error(404)
|
|
98
|
+
return
|
|
99
|
+
self.server.query = {k: v[0] for k, v in parse_qs(url.query).items()} # type: ignore[attr-defined]
|
|
100
|
+
ok = "code" in self.server.query # type: ignore[attr-defined]
|
|
101
|
+
text = "Signed in to Databay. You can close this tab." if ok else "Sign-in failed; see the terminal."
|
|
102
|
+
body = f"<!doctype html><title>Databay CLI</title><p>{text}</p>".encode()
|
|
103
|
+
self.send_response(200)
|
|
104
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
105
|
+
self.send_header("Content-Length", str(len(body)))
|
|
106
|
+
self.end_headers()
|
|
107
|
+
self.wfile.write(body)
|
|
108
|
+
|
|
109
|
+
def log_message(self, format: str, *args: Any) -> None: # noqa: A002, ANN401, ARG002 — the stdlib signature
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def browser_flow(
|
|
114
|
+
http: httpx.Client,
|
|
115
|
+
endpoints: oidc.Endpoints,
|
|
116
|
+
client_id: str,
|
|
117
|
+
console: Console,
|
|
118
|
+
) -> SignIn:
|
|
119
|
+
verifier, challenge = oidc.pkce()
|
|
120
|
+
state = secrets.token_urlsafe(24)
|
|
121
|
+
server = HTTPServer(("127.0.0.1", 0), _Callback)
|
|
122
|
+
server.query = None # type: ignore[attr-defined]
|
|
123
|
+
redirect_uri = f"http://127.0.0.1:{server.server_address[1]}/callback"
|
|
124
|
+
url = (
|
|
125
|
+
endpoints.authorization
|
|
126
|
+
+ "?"
|
|
127
|
+
+ urlencode(
|
|
128
|
+
{
|
|
129
|
+
"response_type": "code",
|
|
130
|
+
"client_id": client_id,
|
|
131
|
+
"redirect_uri": redirect_uri,
|
|
132
|
+
"scope": oidc.LOGIN_SCOPE,
|
|
133
|
+
"state": state,
|
|
134
|
+
"code_challenge": challenge,
|
|
135
|
+
"code_challenge_method": "S256",
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
try:
|
|
140
|
+
console.inform(
|
|
141
|
+
"browser_login",
|
|
142
|
+
{"url": url},
|
|
143
|
+
f"Opening the Databay sign-in page in your browser. If it does not open, visit:\n {url}",
|
|
144
|
+
)
|
|
145
|
+
webbrowser.open(url)
|
|
146
|
+
deadline = time.monotonic() + SETTINGS.browser_login_timeout_s
|
|
147
|
+
while server.query is None: # type: ignore[attr-defined]
|
|
148
|
+
left = deadline - time.monotonic()
|
|
149
|
+
if left <= 0:
|
|
150
|
+
raise DatabayProfileError(
|
|
151
|
+
f"the browser did not return to {redirect_uri} within {SETTINGS.browser_login_timeout_s:g}s",
|
|
152
|
+
code="login_timeout",
|
|
153
|
+
)
|
|
154
|
+
server.timeout = left
|
|
155
|
+
server.handle_request()
|
|
156
|
+
finally:
|
|
157
|
+
server.server_close()
|
|
158
|
+
query: dict[str, str] = server.query # type: ignore[attr-defined]
|
|
159
|
+
if query.get("state") != state:
|
|
160
|
+
raise DatabayProfileError(
|
|
161
|
+
"the browser came back with another sign-in's state; nothing was stored", code="login_failed"
|
|
162
|
+
)
|
|
163
|
+
if "error" in query:
|
|
164
|
+
raise _refused(oidc.OAuthRefusal(f"browser sign-in: {query['error']}", error=query["error"]))
|
|
165
|
+
if "code" not in query:
|
|
166
|
+
raise DatabayProfileError(
|
|
167
|
+
"the browser came back without a code or an error; nothing was stored", code="login_failed"
|
|
168
|
+
)
|
|
169
|
+
issued = oidc.grant(
|
|
170
|
+
http,
|
|
171
|
+
endpoints.token,
|
|
172
|
+
{
|
|
173
|
+
"grant_type": "authorization_code",
|
|
174
|
+
"client_id": client_id,
|
|
175
|
+
"code": query["code"],
|
|
176
|
+
"redirect_uri": redirect_uri,
|
|
177
|
+
"code_verifier": verifier,
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
return _checked(endpoints, issued, offline=True)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def device_flow(
|
|
184
|
+
http: httpx.Client,
|
|
185
|
+
endpoints: oidc.Endpoints,
|
|
186
|
+
client_id: str,
|
|
187
|
+
console: Console,
|
|
188
|
+
sleep: Callable[[float], None] = time.sleep,
|
|
189
|
+
) -> SignIn:
|
|
190
|
+
verifier, challenge = oidc.pkce()
|
|
191
|
+
started = oidc.start_device(http, endpoints.device, client_id, challenge)
|
|
192
|
+
link = started.get("verification_uri_complete") or started["verification_uri"]
|
|
193
|
+
console.inform(
|
|
194
|
+
"device_login",
|
|
195
|
+
{
|
|
196
|
+
"verification_uri": started["verification_uri"],
|
|
197
|
+
"verification_uri_complete": started.get("verification_uri_complete"),
|
|
198
|
+
"user_code": started["user_code"],
|
|
199
|
+
"expires_in": started["expires_in"],
|
|
200
|
+
},
|
|
201
|
+
f"To sign in, open {link}\nand check that it shows the code {started['user_code']}.",
|
|
202
|
+
)
|
|
203
|
+
interval = float(started.get("interval", DEVICE_POLL_DEFAULT_S))
|
|
204
|
+
deadline = time.monotonic() + started["expires_in"]
|
|
205
|
+
data = {
|
|
206
|
+
"grant_type": oidc.DEVICE_GRANT,
|
|
207
|
+
"client_id": client_id,
|
|
208
|
+
"device_code": started["device_code"],
|
|
209
|
+
"code_verifier": verifier,
|
|
210
|
+
}
|
|
211
|
+
while True:
|
|
212
|
+
if time.monotonic() >= deadline:
|
|
213
|
+
raise DatabayProfileError("the sign-in code expired before it was approved", code="expired_token")
|
|
214
|
+
sleep(interval)
|
|
215
|
+
try:
|
|
216
|
+
issued = oidc.grant(http, endpoints.token, data)
|
|
217
|
+
except oidc.OAuthRefusal as exc:
|
|
218
|
+
if exc.error == "authorization_pending":
|
|
219
|
+
continue
|
|
220
|
+
if exc.error == "slow_down":
|
|
221
|
+
interval += DEVICE_SLOW_DOWN_S
|
|
222
|
+
continue
|
|
223
|
+
raise _refused(exc) from None
|
|
224
|
+
return _checked(endpoints, issued, offline=True)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def api_key_flow(http: httpx.Client, endpoints: oidc.Endpoints, client_id: str, secret: str) -> SignIn:
|
|
228
|
+
issued = oidc.grant(
|
|
229
|
+
http, endpoints.token, {"grant_type": "client_credentials", "client_id": client_id, "client_secret": secret}
|
|
230
|
+
)
|
|
231
|
+
return _checked(endpoints, issued, offline=False)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def credential(server: str, info: ServerInfo, signed: SignIn, client_id: str, secret: str | None) -> Credential:
|
|
235
|
+
return Credential(
|
|
236
|
+
kind="login" if secret is None else "api_key",
|
|
237
|
+
server=server,
|
|
238
|
+
sub=signed.sub,
|
|
239
|
+
issuer=info.issuer,
|
|
240
|
+
client_id=client_id,
|
|
241
|
+
access_token=signed.issued.access_token,
|
|
242
|
+
expires_at=signed.issued.expires_at,
|
|
243
|
+
refresh_token=signed.issued.refresh_token if secret is None else None,
|
|
244
|
+
client_secret=secret,
|
|
245
|
+
)
|
databay/model_base.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes of the generated models.
|
|
3
|
+
|
|
4
|
+
A response model ignores fields it does not know, so a field a newer server adds never breaks
|
|
5
|
+
this client. A response enum is open: a value it does not know becomes an ``Unknown(<value>)``
|
|
6
|
+
member with ``is_unknown`` set, never one of the known members. Code that acts on an enum value
|
|
7
|
+
(a wait) treats an unknown one as an error; display shows it as it came.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from enum import StrEnum
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, ConfigDict
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ResponseModel(BaseModel):
|
|
19
|
+
model_config = ConfigDict(extra="ignore", populate_by_name=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RequestModel(BaseModel):
|
|
23
|
+
"""A schema only requests use: a field it does not declare is an error, caught before sending."""
|
|
24
|
+
|
|
25
|
+
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class OpenStrEnum(StrEnum):
|
|
29
|
+
@classmethod
|
|
30
|
+
def _missing_(cls, value: Any) -> OpenStrEnum | None: # noqa: ANN401 — whatever the caller passed
|
|
31
|
+
if not isinstance(value, str):
|
|
32
|
+
return None
|
|
33
|
+
member = str.__new__(cls, value)
|
|
34
|
+
member._name_ = f"Unknown({value})"
|
|
35
|
+
member._value_ = value
|
|
36
|
+
return member
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def is_unknown(self) -> bool:
|
|
40
|
+
return self._name_.startswith("Unknown(")
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Models of the ``alerting_webhook`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from databay.model_base import OpenStrEnum, RequestModel, ResponseModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AlertingWebhookType(OpenStrEnum):
|
|
12
|
+
DISCORD = 'discord'
|
|
13
|
+
TELEGRAM = 'telegram'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AlertingWebhookCreateScope(OpenStrEnum):
|
|
17
|
+
PROJECT = 'project'
|
|
18
|
+
ORGANIZATION = 'organization'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AlertingMessage(RequestModel):
|
|
22
|
+
body: str
|
|
23
|
+
title: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AlertingWebhookCreate(RequestModel):
|
|
27
|
+
name: str
|
|
28
|
+
scope: AlertingWebhookCreateScope | None = None
|
|
29
|
+
tags: list[str] | None = None
|
|
30
|
+
type: AlertingWebhookType
|
|
31
|
+
url: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AlertingWebhookResponse(ResponseModel):
|
|
35
|
+
editable: bool = False
|
|
36
|
+
name: str
|
|
37
|
+
org_wide: bool = True
|
|
38
|
+
project_id: str | None = None
|
|
39
|
+
tags: list[str] | None = None
|
|
40
|
+
type: AlertingWebhookType
|
|
41
|
+
url: str | None = None
|
|
42
|
+
url_masked: str = ''
|
|
43
|
+
uuid: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
AlertingWebhookListResponse = list[AlertingWebhookResponse]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Models of the ``alerting_webhook_undelivered`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from databay.model_base import ResponseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UndeliveredAlertResponse(ResponseModel):
|
|
14
|
+
body: str
|
|
15
|
+
failed_at: datetime
|
|
16
|
+
replayed_at: datetime | None = None
|
|
17
|
+
tags: list[str] | None = None
|
|
18
|
+
title: str
|
|
19
|
+
uuid: str
|
|
20
|
+
webhook_uuid: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
AlertingWebhookUndeliveredListResponse = list[UndeliveredAlertResponse]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Models of the ``announcement`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from databay.model_base import OpenStrEnum, ResponseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AnnouncementResponseLocale(OpenStrEnum):
|
|
14
|
+
EN = 'en'
|
|
15
|
+
FA = 'fa'
|
|
16
|
+
FR = 'fr'
|
|
17
|
+
ES = 'es'
|
|
18
|
+
DE = 'de'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AnnouncementResponseSeverity(OpenStrEnum):
|
|
22
|
+
INFO = 'info'
|
|
23
|
+
WARNING = 'warning'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AnnouncementResponse(ResponseModel):
|
|
27
|
+
body: str
|
|
28
|
+
is_read: bool
|
|
29
|
+
locale: AnnouncementResponseLocale
|
|
30
|
+
severity: AnnouncementResponseSeverity
|
|
31
|
+
starts_at: datetime
|
|
32
|
+
title: str
|
|
33
|
+
uuid: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AnnouncementListResponse(ResponseModel):
|
|
37
|
+
items: list[AnnouncementResponse]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Models of the ``api_key`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from databay.model_base import OpenStrEnum, RequestModel, ResponseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApiKeyCreateRequestRole(OpenStrEnum):
|
|
14
|
+
READER = 'reader'
|
|
15
|
+
EDITOR = 'editor'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ApiKeyCreatedRole(OpenStrEnum):
|
|
19
|
+
READER = 'reader'
|
|
20
|
+
EDITOR = 'editor'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ApiKeyCreatedStatus(OpenStrEnum):
|
|
24
|
+
ACTIVE = 'active'
|
|
25
|
+
REVOKED = 'revoked'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ApiKeyOutRole(OpenStrEnum):
|
|
29
|
+
READER = 'reader'
|
|
30
|
+
EDITOR = 'editor'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ApiKeyOutStatus(OpenStrEnum):
|
|
34
|
+
ACTIVE = 'active'
|
|
35
|
+
REVOKED = 'revoked'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ApiKeyCreateRequest(RequestModel):
|
|
39
|
+
name: str
|
|
40
|
+
project_ids: list[str]
|
|
41
|
+
role: ApiKeyCreateRequestRole
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ApiKeyCreated(ResponseModel):
|
|
45
|
+
client_id: str
|
|
46
|
+
client_secret: str
|
|
47
|
+
created_at: datetime
|
|
48
|
+
created_by: str
|
|
49
|
+
id: str
|
|
50
|
+
name: str
|
|
51
|
+
project_ids: list[str]
|
|
52
|
+
revoked_at: datetime | None
|
|
53
|
+
role: ApiKeyCreatedRole
|
|
54
|
+
rotated_at: datetime | None
|
|
55
|
+
service_account_id: str
|
|
56
|
+
status: ApiKeyCreatedStatus
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ApiKeyOut(ResponseModel):
|
|
60
|
+
client_id: str
|
|
61
|
+
created_at: datetime
|
|
62
|
+
created_by: str
|
|
63
|
+
id: str
|
|
64
|
+
name: str
|
|
65
|
+
project_ids: list[str]
|
|
66
|
+
revoked_at: datetime | None
|
|
67
|
+
role: ApiKeyOutRole
|
|
68
|
+
rotated_at: datetime | None
|
|
69
|
+
service_account_id: str
|
|
70
|
+
status: ApiKeyOutStatus
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ApiKeyList(ResponseModel):
|
|
74
|
+
keys: list[ApiKeyOut]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ApiKeyRotated(ResponseModel):
|
|
78
|
+
client_id: str
|
|
79
|
+
client_secret: str
|
|
80
|
+
id: str
|
|
81
|
+
rotated_at: datetime
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Models of the ``api_key_project`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from databay.model_base import OpenStrEnum, ResponseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApiKeyBiConnectionOutEndpoint(OpenStrEnum):
|
|
14
|
+
ON = 'on'
|
|
15
|
+
OFF = 'off'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ApiKeyBiConnectionOut(ResponseModel):
|
|
19
|
+
database: str | None
|
|
20
|
+
endpoint: ApiKeyBiConnectionOutEndpoint
|
|
21
|
+
host: str | None
|
|
22
|
+
port: int | None
|
|
23
|
+
sslmode: Literal['verify-full'] | None
|
|
24
|
+
username: str | None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ApiKeyProjectOut(ResponseModel):
|
|
28
|
+
key_id: str
|
|
29
|
+
project_id: str
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Models of the ``backfill_run`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from datetime import date
|
|
9
|
+
|
|
10
|
+
from databay.model_base import OpenStrEnum, RequestModel, ResponseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BackfillV2CreateSnapshotType(OpenStrEnum):
|
|
14
|
+
INCREMENTAL = 'incremental'
|
|
15
|
+
BLOCKING = 'blocking'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BackfillRunCancelResponse(ResponseModel):
|
|
19
|
+
pipe_uuid: str
|
|
20
|
+
state: str
|
|
21
|
+
uuid: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BackfillV2Create(RequestModel):
|
|
25
|
+
cursor_field: str | None = None
|
|
26
|
+
pipe_uuid: str
|
|
27
|
+
snapshot_type: BackfillV2CreateSnapshotType = 'incremental'
|
|
28
|
+
start_date: date | None = None
|
|
29
|
+
where_clause: str | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BackfillV2Response(ResponseModel):
|
|
33
|
+
clamped: bool = False
|
|
34
|
+
cursor_field: str | None = None
|
|
35
|
+
job_type: str = 'sync'
|
|
36
|
+
notices: list[str] | None = None
|
|
37
|
+
pipe_uuid: str
|
|
38
|
+
run_uuid: str | None = None
|
|
39
|
+
start_date: str | None = None
|
|
40
|
+
status: str
|
|
41
|
+
stream_ref: str | None = None
|
databay/models/canvas.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Models of the ``canvas`` noun.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from databay.model_base import OpenStrEnum, RequestModel, ResponseModel
|
|
12
|
+
from databay.models.shared import PipeDestinationUnavailableCode
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ActivationResultStatus(OpenStrEnum):
|
|
16
|
+
CREATED = 'created'
|
|
17
|
+
EXISTING = 'existing'
|
|
18
|
+
FAILED = 'failed'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ActivationSecret(RequestModel):
|
|
22
|
+
mask_secret: str | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Activate(RequestModel):
|
|
26
|
+
acknowledge_org_visible: bool = False
|
|
27
|
+
item_ids: list[str] | None = None
|
|
28
|
+
revision: int
|
|
29
|
+
secrets: dict[str, ActivationSecret] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ActivationError(ResponseModel):
|
|
33
|
+
code: str
|
|
34
|
+
detail: Any
|
|
35
|
+
status_code: int
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ActivationResult(ResponseModel):
|
|
39
|
+
error: ActivationError | None
|
|
40
|
+
item_id: str
|
|
41
|
+
pipeline_id: str | None
|
|
42
|
+
status: ActivationResultStatus
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ActivationOut(ResponseModel):
|
|
46
|
+
results: list[ActivationResult]
|
|
47
|
+
revision: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CanvasSummary(ResponseModel):
|
|
51
|
+
draft_count: int
|
|
52
|
+
pipeline_count: int
|
|
53
|
+
project_id: str
|
|
54
|
+
revision: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PipeVolumeOut(ResponseModel):
|
|
58
|
+
pipe_uuid: str
|
|
59
|
+
stored_rows: int | None = None
|
|
60
|
+
unavailable_code: PipeDestinationUnavailableCode | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ProjectVolumes(ResponseModel):
|
|
64
|
+
measured_at: datetime
|
|
65
|
+
pipes: list[PipeVolumeOut]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
CanvasListResponse = list[CanvasSummary]
|