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/cli/auth.py
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""``databay login`` and ``databay logout``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from databay.cli.common import common_options, console_for, http_client, login_options
|
|
12
|
+
from databay.credentials import TOKEN_STORES
|
|
13
|
+
from databay.settings import DEFAULT_PROFILE, DEFAULT_TOKEN_STORE
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
import httpx
|
|
17
|
+
|
|
18
|
+
from databay.cli.console import Console
|
|
19
|
+
from databay.credentials import Credential, TokenStore
|
|
20
|
+
|
|
21
|
+
LOGIN_HELP = f"""Sign in to a Databay server and save it as a profile.
|
|
22
|
+
|
|
23
|
+
\b
|
|
24
|
+
A person signs in in the browser (authorization code with PKCE, back to a
|
|
25
|
+
loopback port), or with --device by opening a link on any device. Over SSH
|
|
26
|
+
or without a display the device flow is used, and the CLI says so.
|
|
27
|
+
\b
|
|
28
|
+
An org API key signs in with --api-key-id and its secret from
|
|
29
|
+
--api-key-env NAME or --api-key-stdin; never as a flag value.
|
|
30
|
+
\b
|
|
31
|
+
A person who belongs to several organizations picks the active one with
|
|
32
|
+
--org KEY_OR_NAME; without it, the CLI asks on a terminal when none is
|
|
33
|
+
active yet, and fails with org_choice_required when it cannot ask.
|
|
34
|
+
\b
|
|
35
|
+
Tokens go to the OS keyring; --token-store file keeps them in a 0600 file
|
|
36
|
+
under the config dir instead. The profile (default: {DEFAULT_PROFILE}) becomes
|
|
37
|
+
the current one when there is none yet. Signing in again revokes the
|
|
38
|
+
refresh token it replaces."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _server(url: str) -> str:
|
|
42
|
+
from databay.errors import DatabayUsageError # noqa: PLC0415
|
|
43
|
+
|
|
44
|
+
if not url.startswith(("https://", "http://")):
|
|
45
|
+
raise DatabayUsageError(f"--server must be an http(s) URL, got {url!r}")
|
|
46
|
+
return url.rstrip("/")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _api_key_secret(key_id: str | None, env_name: str | None, from_stdin: bool) -> str | None:
|
|
50
|
+
from databay.errors import DatabayUsageError # noqa: PLC0415
|
|
51
|
+
|
|
52
|
+
if key_id is None:
|
|
53
|
+
if env_name or from_stdin:
|
|
54
|
+
raise DatabayUsageError("--api-key-env and --api-key-stdin go with --api-key-id")
|
|
55
|
+
return None
|
|
56
|
+
if bool(env_name) == from_stdin:
|
|
57
|
+
raise DatabayUsageError(
|
|
58
|
+
"--api-key-id needs the key's secret from exactly one of --api-key-env NAME or --api-key-stdin",
|
|
59
|
+
code="secret_required",
|
|
60
|
+
)
|
|
61
|
+
if env_name:
|
|
62
|
+
secret = os.environ.get(env_name, "")
|
|
63
|
+
if not secret:
|
|
64
|
+
raise DatabayUsageError(
|
|
65
|
+
f"--api-key-env names ${env_name}, which is not set or empty", code="secret_required"
|
|
66
|
+
)
|
|
67
|
+
return secret
|
|
68
|
+
secret = sys.stdin.read().strip()
|
|
69
|
+
if not secret:
|
|
70
|
+
raise DatabayUsageError("--api-key-stdin: stdin was empty", code="secret_required")
|
|
71
|
+
return secret
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _previous(store: TokenStore, key: str, console: Console) -> Credential | None:
|
|
75
|
+
"""What ``store`` holds under ``key`` before it is overwritten; an unreadable entry is reported and replaced."""
|
|
76
|
+
from databay.errors import DatabayProfileError # noqa: PLC0415
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
return store.get(key)
|
|
80
|
+
except DatabayProfileError as exc:
|
|
81
|
+
if exc.code == "keyring_unavailable":
|
|
82
|
+
raise
|
|
83
|
+
console.event("credential_replaced", {"store": store.kind, "reason": exc.code})
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def revoke(http: httpx.Client, record: Credential) -> None:
|
|
88
|
+
"""Revoke the refresh token of a person's credential at Keycloak; an API key's credential has none."""
|
|
89
|
+
from databay import oidc # noqa: PLC0415
|
|
90
|
+
from databay.errors import DatabayError # noqa: PLC0415
|
|
91
|
+
|
|
92
|
+
if record.refresh_token is None:
|
|
93
|
+
return
|
|
94
|
+
endpoints = oidc.discover(http, record.issuer)
|
|
95
|
+
if endpoints.revocation is None:
|
|
96
|
+
raise DatabayError(f"{record.issuer} names no revocation endpoint", code="revoke_failed")
|
|
97
|
+
oidc.revoke(http, endpoints.revocation, record.client_id, record.refresh_token)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _revoke_superseded(http: httpx.Client, records: list[Credential], console: Console) -> int:
|
|
101
|
+
"""Revoke refresh tokens a new sign-in replaced; a failure is reported, the sign-in still stands."""
|
|
102
|
+
from databay.errors import DatabayError # noqa: PLC0415
|
|
103
|
+
|
|
104
|
+
revoked = 0
|
|
105
|
+
for record in records:
|
|
106
|
+
try:
|
|
107
|
+
revoke(http, record)
|
|
108
|
+
except DatabayError as exc:
|
|
109
|
+
console.inform(
|
|
110
|
+
"revoke_failed",
|
|
111
|
+
{"sub": record.sub, "error": exc.code},
|
|
112
|
+
f"warning: could not revoke the replaced session ({exc.code}); "
|
|
113
|
+
"ask whoever runs the server to revoke it (docs/cli.md, Sign out)",
|
|
114
|
+
)
|
|
115
|
+
continue
|
|
116
|
+
revoked += record.refresh_token is not None
|
|
117
|
+
return revoked
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _save(name: str, server: str, record: Credential, store: TokenStore, console: Console) -> dict[str, Any]:
|
|
121
|
+
"""
|
|
122
|
+
Store ``record`` and point profile ``name`` at it; return what the new sign-in replaced.
|
|
123
|
+
|
|
124
|
+
Every profile that names the same credential moves to ``store`` with it. The profile's previous
|
|
125
|
+
credential, when nothing names it any more, is deleted and listed for revocation.
|
|
126
|
+
"""
|
|
127
|
+
from databay.credentials import credential_key, open_store # noqa: PLC0415
|
|
128
|
+
from databay.locks import locked # noqa: PLC0415
|
|
129
|
+
from databay.profiles import STORED_AUTH, all_profiles, edit_config # noqa: PLC0415
|
|
130
|
+
|
|
131
|
+
key = credential_key(server, record.sub)
|
|
132
|
+
_, profiles = all_profiles()
|
|
133
|
+
replaced: list[Credential] = []
|
|
134
|
+
with locked(key):
|
|
135
|
+
for kind in {p.token_store for p in profiles if p.credential == key} - {store.kind, None}:
|
|
136
|
+
other = open_store(kind)
|
|
137
|
+
previous = _previous(other, key, console)
|
|
138
|
+
if previous is not None:
|
|
139
|
+
replaced.append(previous)
|
|
140
|
+
other.delete(key)
|
|
141
|
+
previous = _previous(store, key, console)
|
|
142
|
+
if previous is not None:
|
|
143
|
+
replaced.append(previous)
|
|
144
|
+
store.put(key, record)
|
|
145
|
+
|
|
146
|
+
def change(data: dict[str, Any]) -> tuple[str | None, str | None, bool]:
|
|
147
|
+
entries = data["profiles"]
|
|
148
|
+
old = entries.get(name, {})
|
|
149
|
+
entry = {"server": server, "auth": record.kind, "credential": key, "token_store": store.kind}
|
|
150
|
+
if old.get("server") == server and old.get("active_project"):
|
|
151
|
+
entry["active_project"] = old["active_project"]
|
|
152
|
+
entries[name] = entry
|
|
153
|
+
for other in entries.values():
|
|
154
|
+
if other.get("credential") == key:
|
|
155
|
+
other["token_store"] = store.kind
|
|
156
|
+
became_current = data.get("current_profile") is None
|
|
157
|
+
if became_current:
|
|
158
|
+
data["current_profile"] = name
|
|
159
|
+
dropped = old.get("credential") if old.get("auth") in STORED_AUTH else None
|
|
160
|
+
if dropped == key or any(e.get("credential") == dropped for e in entries.values()):
|
|
161
|
+
dropped = None
|
|
162
|
+
return dropped, old.get("token_store"), became_current or data["current_profile"] == name
|
|
163
|
+
|
|
164
|
+
dropped, dropped_store, current = edit_config(change)
|
|
165
|
+
if dropped is not None and dropped_store is not None:
|
|
166
|
+
other = open_store(dropped_store)
|
|
167
|
+
with locked(dropped):
|
|
168
|
+
previous = _previous(other, dropped, console)
|
|
169
|
+
other.delete(dropped)
|
|
170
|
+
if previous is not None:
|
|
171
|
+
replaced.append(previous)
|
|
172
|
+
return {
|
|
173
|
+
"replaced": [r for r in replaced if r.refresh_token is not None and r.refresh_token != record.refresh_token],
|
|
174
|
+
"current": current,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _choose_org(
|
|
179
|
+
name: str, wanted: str | None, values: dict[str, Any], console: Console, state: dict[str, Any]
|
|
180
|
+
) -> str | None:
|
|
181
|
+
"""
|
|
182
|
+
The organization the new sign-in acts in: ``wanted``, else the stored one, else the person's choice.
|
|
183
|
+
|
|
184
|
+
Runs after the credential is saved, so a refusal here leaves the profile signed in.
|
|
185
|
+
"""
|
|
186
|
+
from databay.cli.org import ( # noqa: PLC0415
|
|
187
|
+
choose_interactively,
|
|
188
|
+
interactive,
|
|
189
|
+
list_orgs,
|
|
190
|
+
report_no_usable_org,
|
|
191
|
+
resolve_org,
|
|
192
|
+
switch_to,
|
|
193
|
+
)
|
|
194
|
+
from databay.cli.runner import session_for # noqa: PLC0415
|
|
195
|
+
from databay.errors import DatabayUsageError # noqa: PLC0415
|
|
196
|
+
from databay.profiles import load_profile # noqa: PLC0415
|
|
197
|
+
from databay.stored_token import stored_token # noqa: PLC0415
|
|
198
|
+
|
|
199
|
+
profile = load_profile(name)
|
|
200
|
+
# The credential just saved, even where $DATABAY_TOKEN is set: it is the sign-in this command made.
|
|
201
|
+
session = session_for(profile, stored_token(profile, state.get("http")), values, console, state)
|
|
202
|
+
try:
|
|
203
|
+
listing = list_orgs(session)
|
|
204
|
+
if wanted is not None:
|
|
205
|
+
chosen = resolve_org(listing, wanted, profile.server)
|
|
206
|
+
elif not listing.needs_org_choice:
|
|
207
|
+
if listing.active_org is None and listing.usable_orgs == 0:
|
|
208
|
+
report_no_usable_org(listing, profile.server, console)
|
|
209
|
+
return listing.active_org
|
|
210
|
+
elif interactive(console):
|
|
211
|
+
chosen = choose_interactively(listing, console)
|
|
212
|
+
else:
|
|
213
|
+
raise DatabayUsageError(
|
|
214
|
+
f"signed in to {profile.server}, but you belong to {len(listing.organizations)} organizations "
|
|
215
|
+
"and none is active; pass --org KEY_OR_NAME",
|
|
216
|
+
code="org_choice_required",
|
|
217
|
+
details={"organizations": [o.organization for o in listing.organizations]},
|
|
218
|
+
next_command="databay org list",
|
|
219
|
+
)
|
|
220
|
+
return switch_to(session, profile, chosen, console, state)["organization"]
|
|
221
|
+
finally:
|
|
222
|
+
session.transport.close()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@click.command("login", help=LOGIN_HELP, short_help="Sign in and save a profile")
|
|
226
|
+
@click.option("--server", required=True, help="the Databay server URL, e.g. https://databay.example.com")
|
|
227
|
+
@click.option("--device", is_flag=True, default=False, help="sign in with a link and code instead of the browser")
|
|
228
|
+
@click.option(
|
|
229
|
+
"--token-store",
|
|
230
|
+
type=click.Choice(TOKEN_STORES),
|
|
231
|
+
default=DEFAULT_TOKEN_STORE,
|
|
232
|
+
show_default=True,
|
|
233
|
+
help="where tokens are kept",
|
|
234
|
+
)
|
|
235
|
+
@click.option("--api-key-id", default=None, metavar="CLIENT_ID", help="sign in as an org API key (apikey-<org>-<id>)")
|
|
236
|
+
@click.option("--api-key-env", default=None, metavar="NAME", help="the variable that holds the API key's secret")
|
|
237
|
+
@click.option("--api-key-stdin", is_flag=True, default=False, help="read the API key's secret from stdin")
|
|
238
|
+
@click.option(
|
|
239
|
+
"--org", "org", default=None, metavar="KEY_OR_NAME", help="the organization to make active after signing in"
|
|
240
|
+
)
|
|
241
|
+
@login_options
|
|
242
|
+
@click.pass_context
|
|
243
|
+
def login(
|
|
244
|
+
ctx: click.Context,
|
|
245
|
+
server: str,
|
|
246
|
+
device: bool,
|
|
247
|
+
token_store: str,
|
|
248
|
+
api_key_id: str | None,
|
|
249
|
+
api_key_env: str | None,
|
|
250
|
+
api_key_stdin: bool,
|
|
251
|
+
org: str | None,
|
|
252
|
+
**values: Any, # noqa: ANN401
|
|
253
|
+
) -> None:
|
|
254
|
+
from databay import compat, login_flow, oidc # noqa: PLC0415
|
|
255
|
+
from databay.cli.output import emit # noqa: PLC0415
|
|
256
|
+
from databay.credentials import open_store # noqa: PLC0415
|
|
257
|
+
from databay.errors import DatabayUsageError # noqa: PLC0415
|
|
258
|
+
from databay.profiles import check_name # noqa: PLC0415
|
|
259
|
+
from databay.tokens import NoToken # noqa: PLC0415
|
|
260
|
+
from databay.transport import Transport # noqa: PLC0415
|
|
261
|
+
|
|
262
|
+
console, state = console_for(ctx, values)
|
|
263
|
+
name = check_name(values.get("profile") or DEFAULT_PROFILE)
|
|
264
|
+
server = _server(server)
|
|
265
|
+
state["context"] = {"server": server, "profile": name}
|
|
266
|
+
if device and api_key_id:
|
|
267
|
+
raise DatabayUsageError("--device signs a person in; an API key signs in with --api-key-id alone")
|
|
268
|
+
if org is not None and api_key_id:
|
|
269
|
+
raise DatabayUsageError("--org chooses a person's organization; an API key belongs to one organization")
|
|
270
|
+
secret = _api_key_secret(api_key_id, api_key_env, api_key_stdin)
|
|
271
|
+
store = open_store(token_store)
|
|
272
|
+
store.ready()
|
|
273
|
+
with http_client(state) as http:
|
|
274
|
+
transport = Transport(
|
|
275
|
+
server=server,
|
|
276
|
+
tokens=NoToken(),
|
|
277
|
+
client_kind="cli",
|
|
278
|
+
retries=not values.get("no_retry"),
|
|
279
|
+
verbose=bool(values.get("verbose")),
|
|
280
|
+
events=console.event,
|
|
281
|
+
http=http,
|
|
282
|
+
)
|
|
283
|
+
info, _ = compat.server_info(transport, fresh=True)
|
|
284
|
+
compat.check_server(info)
|
|
285
|
+
endpoints = oidc.discover(http, info.issuer)
|
|
286
|
+
if secret is not None:
|
|
287
|
+
assert api_key_id is not None
|
|
288
|
+
signed = login_flow.api_key_flow(http, endpoints, api_key_id, secret)
|
|
289
|
+
client_id = api_key_id
|
|
290
|
+
else:
|
|
291
|
+
client_id = info.client_id
|
|
292
|
+
reason = (
|
|
293
|
+
None if device else login_flow.device_reason(dict(os.environ), sys.platform, login_flow.local_browser)
|
|
294
|
+
)
|
|
295
|
+
if reason is not None:
|
|
296
|
+
console.inform(
|
|
297
|
+
"device_login_chosen",
|
|
298
|
+
{"reason": reason},
|
|
299
|
+
f"{reason[0].upper()}{reason[1:]}; using device login. Pass --device to choose it directly.",
|
|
300
|
+
)
|
|
301
|
+
flow = login_flow.device_flow if device or reason else login_flow.browser_flow
|
|
302
|
+
signed = flow(http, endpoints, client_id, console)
|
|
303
|
+
record = login_flow.credential(server, info, signed, client_id, secret)
|
|
304
|
+
saved = _save(name, server, record, store, console)
|
|
305
|
+
revoked = _revoke_superseded(http, saved["replaced"], console)
|
|
306
|
+
chosen_org = None if secret is not None else _choose_org(name, org, values, console, state)
|
|
307
|
+
claims = oidc.claims(record.access_token)
|
|
308
|
+
result = {
|
|
309
|
+
"profile": name,
|
|
310
|
+
"server": server,
|
|
311
|
+
"auth": record.kind,
|
|
312
|
+
"user": claims.get("preferred_username"),
|
|
313
|
+
"sub": record.sub,
|
|
314
|
+
"token_store": store.kind,
|
|
315
|
+
"current": saved["current"],
|
|
316
|
+
"revoked_superseded": revoked,
|
|
317
|
+
"org": chosen_org,
|
|
318
|
+
}
|
|
319
|
+
next_command = "databay status" if saved["current"] else f"databay profile use {name}"
|
|
320
|
+
emit(console, {"result": result, "next": next_command}, ())
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@click.command("logout", short_help="Sign out: revoke and delete the profile's credential")
|
|
324
|
+
@common_options
|
|
325
|
+
@click.pass_context
|
|
326
|
+
def logout(ctx: click.Context, **values: Any) -> None: # noqa: ANN401
|
|
327
|
+
"""
|
|
328
|
+
Revoke the profile's refresh token at Keycloak, then delete its stored credential.
|
|
329
|
+
|
|
330
|
+
Every profile that named the credential loses it. When the revocation fails the credential is
|
|
331
|
+
still deleted here, and the command exits 1 with revoke_failed.
|
|
332
|
+
"""
|
|
333
|
+
from databay.cli.output import emit # noqa: PLC0415
|
|
334
|
+
from databay.credentials import open_store # noqa: PLC0415
|
|
335
|
+
from databay.errors import DatabayError, DatabayUsageError # noqa: PLC0415
|
|
336
|
+
from databay.locks import locked # noqa: PLC0415
|
|
337
|
+
from databay.profiles import edit_config, load_profile # noqa: PLC0415
|
|
338
|
+
|
|
339
|
+
console, state = console_for(ctx, values)
|
|
340
|
+
profile = load_profile(values.get("profile"))
|
|
341
|
+
state["context"] = {"server": profile.server, "profile": profile.name}
|
|
342
|
+
if profile.auth == "env":
|
|
343
|
+
raise DatabayUsageError(f"profile {profile.name} reads $DATABAY_TOKEN and stores nothing to sign out of")
|
|
344
|
+
key = profile.credential
|
|
345
|
+
if key is None or profile.token_store is None:
|
|
346
|
+
emit(console, {"result": {"credential": None, "profiles": [], "revoked": False}}, ())
|
|
347
|
+
return
|
|
348
|
+
store = open_store(profile.token_store)
|
|
349
|
+
failure: DatabayError | None = None
|
|
350
|
+
with locked(key):
|
|
351
|
+
record = _previous(store, key, console)
|
|
352
|
+
if record is not None:
|
|
353
|
+
with http_client(state) as http:
|
|
354
|
+
try:
|
|
355
|
+
revoke(http, record)
|
|
356
|
+
except DatabayError as exc:
|
|
357
|
+
failure = exc
|
|
358
|
+
store.delete(key)
|
|
359
|
+
|
|
360
|
+
def change(data: dict[str, Any]) -> list[str]:
|
|
361
|
+
cleared = []
|
|
362
|
+
for other_name, entry in data["profiles"].items():
|
|
363
|
+
if entry.get("credential") == key:
|
|
364
|
+
entry.pop("credential")
|
|
365
|
+
entry.pop("token_store", None)
|
|
366
|
+
cleared.append(other_name)
|
|
367
|
+
return sorted(cleared)
|
|
368
|
+
|
|
369
|
+
cleared = edit_config(change)
|
|
370
|
+
revoked = failure is None and record is not None and record.refresh_token is not None
|
|
371
|
+
emit(console, {"result": {"credential": key, "profiles": cleared, "revoked": revoked}}, ())
|
|
372
|
+
if failure is not None:
|
|
373
|
+
raise DatabayError(
|
|
374
|
+
f"signed out locally, but Keycloak refused to revoke the session: {failure.message}",
|
|
375
|
+
code="revoke_failed",
|
|
376
|
+
details={"cause": failure.code, "profiles": cleared},
|
|
377
|
+
)
|
databay/cli/build.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Click commands from generated ``Operation`` data.
|
|
3
|
+
|
|
4
|
+
Each operation becomes ``databay <noun> <verb>``; a discriminated body, or a body's discriminated
|
|
5
|
+
property, becomes one subcommand per variant (``databay source precheck postgres``, ``databay source
|
|
6
|
+
create postgres``). The flags come from ``databay.cli.flags`` and the
|
|
7
|
+
help text from ``databay.reference``, the same functions ``commands.md`` is rendered with.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
from databay.cli.flags import Flag, command_flags
|
|
17
|
+
from databay.reference import click_help
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from databay.operation import Operation
|
|
21
|
+
|
|
22
|
+
CLICK_TYPES: dict[str, Any] = {"integer": click.INT, "number": click.FLOAT, "string": click.STRING}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _click_type(flag: Flag) -> Any: # noqa: ANN401 — a click ParamType
|
|
26
|
+
if flag.choices:
|
|
27
|
+
return click.Choice(flag.choices)
|
|
28
|
+
if flag.role == "timeout":
|
|
29
|
+
return click.FloatRange(min=0, min_open=True)
|
|
30
|
+
param = flag.param
|
|
31
|
+
if param is None:
|
|
32
|
+
return click.STRING
|
|
33
|
+
kind = param.item_kind if param.kind == "list" else param.kind
|
|
34
|
+
return CLICK_TYPES.get(kind or "string", click.STRING)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def click_parameter(flag: Flag) -> click.Parameter:
|
|
38
|
+
if flag.takes == "argument":
|
|
39
|
+
return click.Argument([flag.dest], required=flag.required, type=_click_type(flag), metavar=flag.name)
|
|
40
|
+
help_text = flag.help + (" [required]" if flag.required else "")
|
|
41
|
+
if flag.takes == "switch":
|
|
42
|
+
return click.Option([flag.name, flag.dest], is_flag=True, default=False, help=help_text)
|
|
43
|
+
if flag.takes == "bool":
|
|
44
|
+
negative = "--no-" + flag.name.removeprefix("--")
|
|
45
|
+
return click.Option([f"{flag.name}/{negative}", flag.dest], default=None, help=help_text)
|
|
46
|
+
return click.Option(
|
|
47
|
+
[flag.name, flag.dest],
|
|
48
|
+
type=_click_type(flag),
|
|
49
|
+
multiple=flag.takes == "multiple",
|
|
50
|
+
default=None,
|
|
51
|
+
help=help_text,
|
|
52
|
+
metavar=flag.metavar,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def operation_command(op: Operation, variant: str | None = None) -> click.Command:
|
|
57
|
+
flags = command_flags(op, variant)
|
|
58
|
+
|
|
59
|
+
@click.pass_context
|
|
60
|
+
def callback(ctx: click.Context, **values: Any) -> None: # noqa: ANN401
|
|
61
|
+
from databay.cli.runner import run # noqa: PLC0415 — the runner loads httpx and pydantic
|
|
62
|
+
|
|
63
|
+
run(ctx, op, variant, flags, values)
|
|
64
|
+
|
|
65
|
+
name = variant if variant is not None else op.verb
|
|
66
|
+
return click.Command(
|
|
67
|
+
name,
|
|
68
|
+
callback=callback,
|
|
69
|
+
params=[click_parameter(flag) for flag in flags],
|
|
70
|
+
help=click_help(op, variant),
|
|
71
|
+
short_help=op.summary,
|
|
72
|
+
no_args_is_help=False,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def verb_command(op: Operation) -> click.Command:
|
|
77
|
+
if op.body is None or op.body.discriminator is None:
|
|
78
|
+
return operation_command(op)
|
|
79
|
+
group = click.Group(op.verb, help=click_help(op), short_help=op.summary)
|
|
80
|
+
for key, _, _ in op.body.variants:
|
|
81
|
+
group.add_command(operation_command(op, key))
|
|
82
|
+
return group
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def noun_group(noun: str, operations: tuple[Operation, ...]) -> click.Group:
|
|
86
|
+
group = click.Group(noun, help=f"The {noun} commands.")
|
|
87
|
+
for op in operations:
|
|
88
|
+
group.add_command(verb_command(op))
|
|
89
|
+
return group
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""``databay alerting-webhook``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import alerting_webhook as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'alerting-webhook',
|
|
11
|
+
(
|
|
12
|
+
_ops.ALERTING_WEBHOOK_CREATE,
|
|
13
|
+
_ops.ALERTING_WEBHOOK_DELETE,
|
|
14
|
+
_ops.ALERTING_WEBHOOK_GET,
|
|
15
|
+
_ops.ALERTING_WEBHOOK_LIST,
|
|
16
|
+
_ops.ALERTING_WEBHOOK_NOTIFY,
|
|
17
|
+
_ops.ALERTING_WEBHOOK_UPDATE,
|
|
18
|
+
),
|
|
19
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""``databay alerting-webhook-tag``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import alerting_webhook_tag as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'alerting-webhook-tag',
|
|
11
|
+
(
|
|
12
|
+
_ops.ALERTING_WEBHOOK_TAG_LIST,
|
|
13
|
+
),
|
|
14
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""``databay alerting-webhook-type``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import alerting_webhook_type as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'alerting-webhook-type',
|
|
11
|
+
(
|
|
12
|
+
_ops.ALERTING_WEBHOOK_TYPE_LIST,
|
|
13
|
+
),
|
|
14
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""``databay alerting-webhook-undelivered``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import alerting_webhook_undelivered as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'alerting-webhook-undelivered',
|
|
11
|
+
(
|
|
12
|
+
_ops.ALERTING_WEBHOOK_UNDELIVERED_LIST,
|
|
13
|
+
_ops.ALERTING_WEBHOOK_UNDELIVERED_REPLAY,
|
|
14
|
+
),
|
|
15
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""``databay announcement``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import announcement as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'announcement',
|
|
11
|
+
(
|
|
12
|
+
_ops.ANNOUNCEMENT_LIST,
|
|
13
|
+
_ops.ANNOUNCEMENT_MARK_ALL_READ,
|
|
14
|
+
_ops.ANNOUNCEMENT_MARK_READ,
|
|
15
|
+
_ops.ANNOUNCEMENT_MARK_UNREAD,
|
|
16
|
+
),
|
|
17
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""``databay api-key``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import api_key as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'api-key',
|
|
11
|
+
(
|
|
12
|
+
_ops.API_KEY_CREATE,
|
|
13
|
+
_ops.API_KEY_GET,
|
|
14
|
+
_ops.API_KEY_LIST,
|
|
15
|
+
_ops.API_KEY_REVOKE,
|
|
16
|
+
_ops.API_KEY_ROTATE,
|
|
17
|
+
),
|
|
18
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""``databay api-key-project``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import api_key_project as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'api-key-project',
|
|
11
|
+
(
|
|
12
|
+
_ops.API_KEY_PROJECT_ADD,
|
|
13
|
+
_ops.API_KEY_PROJECT_BI_CONNECTION,
|
|
14
|
+
_ops.API_KEY_PROJECT_REMOVE,
|
|
15
|
+
),
|
|
16
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""``databay backfill-reason-code``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import backfill_reason_code as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'backfill-reason-code',
|
|
11
|
+
(
|
|
12
|
+
_ops.BACKFILL_REASON_CODE_LIST,
|
|
13
|
+
),
|
|
14
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""``databay backfill-run``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import backfill_run as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'backfill-run',
|
|
11
|
+
(
|
|
12
|
+
_ops.BACKFILL_RUN_CANCEL,
|
|
13
|
+
_ops.BACKFILL_RUN_CREATE,
|
|
14
|
+
_ops.BACKFILL_RUN_GET,
|
|
15
|
+
_ops.BACKFILL_RUN_LIST,
|
|
16
|
+
),
|
|
17
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""``databay canvas``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import canvas as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'canvas',
|
|
11
|
+
(
|
|
12
|
+
_ops.CANVAS_ACTIVATE,
|
|
13
|
+
_ops.CANVAS_LIST,
|
|
14
|
+
_ops.CANVAS_VOLUMES,
|
|
15
|
+
),
|
|
16
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""``databay canvas-graph``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import canvas_graph as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'canvas-graph',
|
|
11
|
+
(
|
|
12
|
+
_ops.CANVAS_GRAPH_GET,
|
|
13
|
+
_ops.CANVAS_GRAPH_PUT,
|
|
14
|
+
),
|
|
15
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""``databay canvas-table-check``: imported only when that noun runs.
|
|
2
|
+
|
|
3
|
+
Generated by python/tools/gen_client.py from contract/core.json; do not edit.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from databay.cli.build import noun_group
|
|
7
|
+
from databay.operations import canvas_table_check as _ops
|
|
8
|
+
|
|
9
|
+
group = noun_group(
|
|
10
|
+
'canvas-table-check',
|
|
11
|
+
(
|
|
12
|
+
_ops.CANVAS_TABLE_CHECK_GET,
|
|
13
|
+
_ops.CANVAS_TABLE_CHECK_START,
|
|
14
|
+
),
|
|
15
|
+
)
|