dagster-dg-cli 1.12.16__py2.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.
- dagster_dg_cli/__init__.py +5 -0
- dagster_dg_cli/api_layer/__init__.py +1 -0
- dagster_dg_cli/api_layer/api/__init__.py +1 -0
- dagster_dg_cli/api_layer/api/agent.py +24 -0
- dagster_dg_cli/api_layer/api/asset.py +58 -0
- dagster_dg_cli/api_layer/api/deployments.py +24 -0
- dagster_dg_cli/api_layer/api/run.py +21 -0
- dagster_dg_cli/api_layer/api/run_event.py +76 -0
- dagster_dg_cli/api_layer/api/schedule.py +54 -0
- dagster_dg_cli/api_layer/api/secret.py +74 -0
- dagster_dg_cli/api_layer/api/sensor.py +52 -0
- dagster_dg_cli/api_layer/graphql_adapter/__init__.py +1 -0
- dagster_dg_cli/api_layer/graphql_adapter/agent.py +108 -0
- dagster_dg_cli/api_layer/graphql_adapter/asset.py +643 -0
- dagster_dg_cli/api_layer/graphql_adapter/deployment.py +145 -0
- dagster_dg_cli/api_layer/graphql_adapter/run.py +60 -0
- dagster_dg_cli/api_layer/graphql_adapter/run_event.py +197 -0
- dagster_dg_cli/api_layer/graphql_adapter/schedule.py +299 -0
- dagster_dg_cli/api_layer/graphql_adapter/secret.py +214 -0
- dagster_dg_cli/api_layer/graphql_adapter/sensor.py +287 -0
- dagster_dg_cli/api_layer/schemas/__init__.py +1 -0
- dagster_dg_cli/api_layer/schemas/agent.py +42 -0
- dagster_dg_cli/api_layer/schemas/asset.py +82 -0
- dagster_dg_cli/api_layer/schemas/deployment.py +30 -0
- dagster_dg_cli/api_layer/schemas/run.py +31 -0
- dagster_dg_cli/api_layer/schemas/run_event.py +61 -0
- dagster_dg_cli/api_layer/schemas/schedule.py +36 -0
- dagster_dg_cli/api_layer/schemas/secret.py +78 -0
- dagster_dg_cli/api_layer/schemas/sensor.py +45 -0
- dagster_dg_cli/cli/__init__.py +76 -0
- dagster_dg_cli/cli/__main__.py +4 -0
- dagster_dg_cli/cli/api/__init__.py +5 -0
- dagster_dg_cli/cli/api/agent.py +111 -0
- dagster_dg_cli/cli/api/asset.py +138 -0
- dagster_dg_cli/cli/api/cli_group.py +33 -0
- dagster_dg_cli/cli/api/client.py +86 -0
- dagster_dg_cli/cli/api/deployment.py +108 -0
- dagster_dg_cli/cli/api/formatters.py +362 -0
- dagster_dg_cli/cli/api/log.py +194 -0
- dagster_dg_cli/cli/api/run.py +88 -0
- dagster_dg_cli/cli/api/run_event.py +140 -0
- dagster_dg_cli/cli/api/schedule.py +192 -0
- dagster_dg_cli/cli/api/secret.py +155 -0
- dagster_dg_cli/cli/api/sensor.py +179 -0
- dagster_dg_cli/cli/api/shared.py +30 -0
- dagster_dg_cli/cli/check.py +219 -0
- dagster_dg_cli/cli/defs_state.py +170 -0
- dagster_dg_cli/cli/dev.py +174 -0
- dagster_dg_cli/cli/entrypoint.py +4 -0
- dagster_dg_cli/cli/launch.py +99 -0
- dagster_dg_cli/cli/list.py +592 -0
- dagster_dg_cli/cli/mcp_server.py +107 -0
- dagster_dg_cli/cli/plus/__init__.py +21 -0
- dagster_dg_cli/cli/plus/constants.py +19 -0
- dagster_dg_cli/cli/plus/create/__init__.py +14 -0
- dagster_dg_cli/cli/plus/create/ci_api_token.py +28 -0
- dagster_dg_cli/cli/plus/create/env.py +221 -0
- dagster_dg_cli/cli/plus/deploy/__init__.py +4 -0
- dagster_dg_cli/cli/plus/deploy/commands.py +721 -0
- dagster_dg_cli/cli/plus/deploy/configure/__init__.py +1 -0
- dagster_dg_cli/cli/plus/deploy/configure/commands.py +495 -0
- dagster_dg_cli/cli/plus/deploy/configure/configure_build_artifacts.py +145 -0
- dagster_dg_cli/cli/plus/deploy/configure/configure_ci.py +318 -0
- dagster_dg_cli/cli/plus/deploy/configure/utils.py +556 -0
- dagster_dg_cli/cli/plus/deploy/deploy_session.py +312 -0
- dagster_dg_cli/cli/plus/deploy/validation.py +69 -0
- dagster_dg_cli/cli/plus/login.py +78 -0
- dagster_dg_cli/cli/plus/pull/__init__.py +12 -0
- dagster_dg_cli/cli/plus/pull/env.py +86 -0
- dagster_dg_cli/cli/scaffold/__init__.py +23 -0
- dagster_dg_cli/cli/scaffold/branch/CLAUDE.md +241 -0
- dagster_dg_cli/cli/scaffold/branch/__init__.py +1 -0
- dagster_dg_cli/cli/scaffold/branch/ai.py +284 -0
- dagster_dg_cli/cli/scaffold/branch/claude/__init__.py +1 -0
- dagster_dg_cli/cli/scaffold/branch/claude/diagnostics.py +349 -0
- dagster_dg_cli/cli/scaffold/branch/claude/sdk_client.py +188 -0
- dagster_dg_cli/cli/scaffold/branch/command.py +439 -0
- dagster_dg_cli/cli/scaffold/branch/constants.py +63 -0
- dagster_dg_cli/cli/scaffold/branch/dg_scaffold_pr_sequencing_plan.md +262 -0
- dagster_dg_cli/cli/scaffold/branch/git.py +182 -0
- dagster_dg_cli/cli/scaffold/branch/models.py +76 -0
- dagster_dg_cli/cli/scaffold/branch/planning.py +382 -0
- dagster_dg_cli/cli/scaffold/branch/pr1_diagnostics_implementation_plan.md +123 -0
- dagster_dg_cli/cli/scaffold/branch/prompts/best_practices.md +70 -0
- dagster_dg_cli/cli/scaffold/branch/prompts/planning_prompt.md +50 -0
- dagster_dg_cli/cli/scaffold/branch/version_utils.py +9 -0
- dagster_dg_cli/cli/scaffold/build_artifacts.py +118 -0
- dagster_dg_cli/cli/scaffold/component.py +80 -0
- dagster_dg_cli/cli/scaffold/defs.py +408 -0
- dagster_dg_cli/cli/scaffold/github_actions.py +116 -0
- dagster_dg_cli/cli/utils.py +597 -0
- dagster_dg_cli/docs/.gitignore +9 -0
- dagster_dg_cli/mcp/__init__.py +0 -0
- dagster_dg_cli/mcp/server.py +308 -0
- dagster_dg_cli/py.typed +1 -0
- dagster_dg_cli/scaffold.py +135 -0
- dagster_dg_cli/templates/COMPONENT_TYPE/COMPONENT_TYPE_NAME_PLACEHOLDER.py.jinja +27 -0
- dagster_dg_cli/templates/build-location-fragment-gitlab.yaml +4 -0
- dagster_dg_cli/templates/build-location-fragment.yaml +16 -0
- dagster_dg_cli/templates/deploy_uv_Dockerfile.jinja +40 -0
- dagster_dg_cli/templates/deploy_uv_editable_Dockerfile.jinja +49 -0
- dagster_dg_cli/templates/hybrid-github-action.yaml +126 -0
- dagster_dg_cli/templates/hybrid-gitlab-ci.yaml +77 -0
- dagster_dg_cli/templates/registry_fragments/github/azure-container-registry-login-fragment.yaml +9 -0
- dagster_dg_cli/templates/registry_fragments/github/dockerhub-login-fragment.yaml +8 -0
- dagster_dg_cli/templates/registry_fragments/github/ecr-login-fragment.yaml +12 -0
- dagster_dg_cli/templates/registry_fragments/github/gcr-login-fragment.yaml +9 -0
- dagster_dg_cli/templates/registry_fragments/github/github-container-registry-login-fragment.yaml +9 -0
- dagster_dg_cli/templates/registry_fragments/gitlab/azure-container-registry-login-fragment.yaml +2 -0
- dagster_dg_cli/templates/registry_fragments/gitlab/dockerhub-login-fragment.yaml +2 -0
- dagster_dg_cli/templates/registry_fragments/gitlab/ecr-login-fragment.yaml +6 -0
- dagster_dg_cli/templates/registry_fragments/gitlab/gcr-login-fragment.yaml +2 -0
- dagster_dg_cli/templates/registry_fragments/gitlab/github-container-registry-login-fragment.yaml +2 -0
- dagster_dg_cli/templates/serverless-github-action.yaml +126 -0
- dagster_dg_cli/templates/serverless-gitlab-ci.yaml +64 -0
- dagster_dg_cli/utils/__init__.py +0 -0
- dagster_dg_cli/utils/claude_utils.py +15 -0
- dagster_dg_cli/utils/plus/__init__.py +0 -0
- dagster_dg_cli/utils/plus/build.py +98 -0
- dagster_dg_cli/utils/plus/defs_state_storage.py +174 -0
- dagster_dg_cli/utils/plus/gql.py +182 -0
- dagster_dg_cli/utils/plus/gql_client.py +100 -0
- dagster_dg_cli/utils/ui.py +87 -0
- dagster_dg_cli/utils/yaml_template_generator.py +419 -0
- dagster_dg_cli/version.py +1 -0
- dagster_dg_cli-1.12.16.dist-info/METADATA +23 -0
- dagster_dg_cli-1.12.16.dist-info/RECORD +130 -0
- dagster_dg_cli-1.12.16.dist-info/WHEEL +5 -0
- dagster_dg_cli-1.12.16.dist-info/entry_points.txt +2 -0
- dagster_dg_cli-1.12.16.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Dagster Plus API client library."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""API client endpoints."""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Agent endpoints - REST-like interface."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.agent import (
|
|
7
|
+
get_agent_via_graphql,
|
|
8
|
+
list_agents_via_graphql,
|
|
9
|
+
)
|
|
10
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from dagster_dg_cli.api_layer.schemas.agent import DgApiAgent, DgApiAgentList
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class DgApiAgentApi:
|
|
18
|
+
client: IGraphQLClient
|
|
19
|
+
|
|
20
|
+
def list_agents(self) -> "DgApiAgentList":
|
|
21
|
+
return list_agents_via_graphql(self.client)
|
|
22
|
+
|
|
23
|
+
def get_agent(self, agent_id: str) -> Optional["DgApiAgent"]:
|
|
24
|
+
return get_agent_via_graphql(self.client, agent_id)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Asset endpoints - REST-like interface."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.asset import (
|
|
7
|
+
get_dg_plus_api_asset_via_graphql,
|
|
8
|
+
get_dg_plus_api_asset_with_status_via_graphql,
|
|
9
|
+
list_dg_plus_api_assets_via_graphql,
|
|
10
|
+
list_dg_plus_api_assets_with_status_via_graphql,
|
|
11
|
+
)
|
|
12
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from dagster_dg_cli.api_layer.schemas.asset import DgApiAsset, DgApiAssetList
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class DgApiAssetApi:
|
|
20
|
+
client: IGraphQLClient
|
|
21
|
+
|
|
22
|
+
def list_assets(
|
|
23
|
+
self,
|
|
24
|
+
limit: int | None = 50,
|
|
25
|
+
cursor: str | None = None,
|
|
26
|
+
view: str | None = None,
|
|
27
|
+
) -> "DgApiAssetList":
|
|
28
|
+
"""List assets with cursor-based pagination and optional view."""
|
|
29
|
+
from dagster_dg_cli.cli.api.asset import DG_API_MAX_ASSET_LIMIT
|
|
30
|
+
|
|
31
|
+
# Enforce max limit constraint
|
|
32
|
+
if limit and limit > DG_API_MAX_ASSET_LIMIT:
|
|
33
|
+
raise ValueError(f"Limit cannot exceed {DG_API_MAX_ASSET_LIMIT}")
|
|
34
|
+
|
|
35
|
+
# Validate view parameter
|
|
36
|
+
if view and view not in ["status"]:
|
|
37
|
+
raise ValueError(f"Invalid view: {view}. Supported views: status")
|
|
38
|
+
|
|
39
|
+
if view == "status":
|
|
40
|
+
return list_dg_plus_api_assets_with_status_via_graphql(
|
|
41
|
+
self.client, limit=limit, cursor=cursor
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
return list_dg_plus_api_assets_via_graphql(self.client, limit=limit, cursor=cursor)
|
|
45
|
+
|
|
46
|
+
def get_asset(self, asset_key: str, view: str | None = None) -> "DgApiAsset":
|
|
47
|
+
"""Get single asset by slash-separated key (e.g., 'foo/bar') with optional view."""
|
|
48
|
+
# Parse "foo/bar" to ["foo", "bar"]
|
|
49
|
+
asset_key_parts = asset_key.split("/")
|
|
50
|
+
|
|
51
|
+
# Validate view parameter
|
|
52
|
+
if view and view not in ["status"]:
|
|
53
|
+
raise ValueError(f"Invalid view: {view}. Supported views: status")
|
|
54
|
+
|
|
55
|
+
if view == "status":
|
|
56
|
+
return get_dg_plus_api_asset_with_status_via_graphql(self.client, asset_key_parts)
|
|
57
|
+
else:
|
|
58
|
+
return get_dg_plus_api_asset_via_graphql(self.client, asset_key_parts)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Deployment endpoints - REST-like interface."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.deployment import (
|
|
7
|
+
get_deployment_by_name_via_graphql,
|
|
8
|
+
list_deployments_via_graphql,
|
|
9
|
+
)
|
|
10
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from dagster_dg_cli.api_layer.schemas.deployment import Deployment, DeploymentList
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class DgApiDeploymentApi:
|
|
18
|
+
client: IGraphQLClient
|
|
19
|
+
|
|
20
|
+
def list_deployments(self) -> "DeploymentList":
|
|
21
|
+
return list_deployments_via_graphql(self.client)
|
|
22
|
+
|
|
23
|
+
def get_deployment(self, name: str) -> "Deployment":
|
|
24
|
+
return get_deployment_by_name_via_graphql(self.client, name)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Run metadata API implementation."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.run import get_run_via_graphql
|
|
7
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from dagster_dg_cli.api_layer.schemas.run import DgApiRun
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class DgApiRunApi:
|
|
15
|
+
"""API for run metadata operations."""
|
|
16
|
+
|
|
17
|
+
client: IGraphQLClient
|
|
18
|
+
|
|
19
|
+
def get_run(self, run_id: str) -> "DgApiRun":
|
|
20
|
+
"""Get run metadata by ID."""
|
|
21
|
+
return get_run_via_graphql(self.client, run_id)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Run events API implementation."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.run_event import get_run_events_via_graphql
|
|
7
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from dagster_dg_cli.api_layer.schemas.run_event import RunEventList
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class DgApiRunEventApi:
|
|
15
|
+
"""API for run events operations."""
|
|
16
|
+
|
|
17
|
+
client: IGraphQLClient
|
|
18
|
+
|
|
19
|
+
def get_events(
|
|
20
|
+
self,
|
|
21
|
+
run_id: str,
|
|
22
|
+
event_types: tuple[str, ...] = (),
|
|
23
|
+
step_keys: tuple[str, ...] = (),
|
|
24
|
+
levels: tuple[str, ...] = (),
|
|
25
|
+
limit: int = 100,
|
|
26
|
+
after_cursor: str | None = None,
|
|
27
|
+
) -> "RunEventList":
|
|
28
|
+
"""Get run events with filtering options."""
|
|
29
|
+
from dagster_dg_cli.api_layer.schemas.run_event import (
|
|
30
|
+
DgApiErrorInfo,
|
|
31
|
+
DgApiRunEvent,
|
|
32
|
+
RunEventLevel,
|
|
33
|
+
RunEventList,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
events_data = get_run_events_via_graphql(
|
|
37
|
+
self.client,
|
|
38
|
+
run_id=run_id,
|
|
39
|
+
limit=limit,
|
|
40
|
+
after_cursor=after_cursor,
|
|
41
|
+
event_types=event_types,
|
|
42
|
+
step_keys=step_keys,
|
|
43
|
+
levels=levels,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Helper function to convert error data to DgApiErrorInfo recursively
|
|
47
|
+
def _convert_error_info(error_data: dict | None) -> DgApiErrorInfo | None:
|
|
48
|
+
if not error_data:
|
|
49
|
+
return None
|
|
50
|
+
return DgApiErrorInfo(
|
|
51
|
+
message=error_data.get("message", ""),
|
|
52
|
+
className=error_data.get("className"),
|
|
53
|
+
stack=error_data.get("stack"),
|
|
54
|
+
cause=_convert_error_info(error_data.get("cause")),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Convert to Pydantic models
|
|
58
|
+
events = [
|
|
59
|
+
DgApiRunEvent(
|
|
60
|
+
run_id=e["runId"],
|
|
61
|
+
message=e["message"],
|
|
62
|
+
timestamp=e["timestamp"],
|
|
63
|
+
level=RunEventLevel[e["level"]],
|
|
64
|
+
step_key=e.get("stepKey"),
|
|
65
|
+
event_type=e.get("eventType"),
|
|
66
|
+
error=_convert_error_info(e.get("error")),
|
|
67
|
+
)
|
|
68
|
+
for e in events_data["events"]
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
return RunEventList(
|
|
72
|
+
items=events,
|
|
73
|
+
total=len(events),
|
|
74
|
+
cursor=events_data.get("cursor"),
|
|
75
|
+
has_more=events_data.get("hasMore", False),
|
|
76
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Schedule API implementation."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.schedule import (
|
|
7
|
+
get_schedule_via_graphql,
|
|
8
|
+
list_schedules_via_graphql,
|
|
9
|
+
)
|
|
10
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from dagster_dg_cli.api_layer.schemas.schedule import DgApiSchedule, DgApiScheduleList
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class DgApiScheduleApi:
|
|
18
|
+
"""API for schedule operations."""
|
|
19
|
+
|
|
20
|
+
client: IGraphQLClient
|
|
21
|
+
|
|
22
|
+
def list_schedules(
|
|
23
|
+
self,
|
|
24
|
+
repository_location_name: str | None = None,
|
|
25
|
+
repository_name: str | None = None,
|
|
26
|
+
) -> "DgApiScheduleList":
|
|
27
|
+
"""List all schedules, optionally filtered by code location and name."""
|
|
28
|
+
return list_schedules_via_graphql(
|
|
29
|
+
self.client,
|
|
30
|
+
repository_location_name=repository_location_name,
|
|
31
|
+
repository_name=repository_name,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def get_schedule(
|
|
35
|
+
self,
|
|
36
|
+
schedule_name: str,
|
|
37
|
+
repository_location_name: str,
|
|
38
|
+
repository_name: str,
|
|
39
|
+
) -> "DgApiSchedule":
|
|
40
|
+
"""Get schedule by name and code location details."""
|
|
41
|
+
return get_schedule_via_graphql(
|
|
42
|
+
self.client,
|
|
43
|
+
schedule_name=schedule_name,
|
|
44
|
+
repository_location_name=repository_location_name,
|
|
45
|
+
repository_name=repository_name,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def get_schedule_by_name(self, schedule_name: str) -> "DgApiSchedule":
|
|
49
|
+
"""Get schedule by name, searching across all code locations."""
|
|
50
|
+
from dagster_dg_cli.api_layer.graphql_adapter.schedule import (
|
|
51
|
+
get_schedule_by_name_via_graphql,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return get_schedule_by_name_via_graphql(self.client, schedule_name=schedule_name)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Secret endpoints - REST-like interface."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.secret import (
|
|
7
|
+
get_secret_via_graphql,
|
|
8
|
+
list_secrets_via_graphql,
|
|
9
|
+
)
|
|
10
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from dagster_dg_cli.api_layer.schemas.secret import DgApiSecret, DgApiSecretList
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class DgApiSecretApi:
|
|
18
|
+
"""Secret API operations."""
|
|
19
|
+
|
|
20
|
+
client: IGraphQLClient
|
|
21
|
+
|
|
22
|
+
def list_secrets(
|
|
23
|
+
self,
|
|
24
|
+
location_name: str | None = None,
|
|
25
|
+
scope: str | None = None,
|
|
26
|
+
limit: int | None = None,
|
|
27
|
+
) -> "DgApiSecretList":
|
|
28
|
+
"""List secrets with optional filtering.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
location_name: Optional filter by code location name
|
|
32
|
+
scope: Optional scope filter ("deployment" or "organization")
|
|
33
|
+
limit: Optional limit on number of results
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
DgApiSecretList: List of secrets (values are never included for security)
|
|
37
|
+
|
|
38
|
+
Note:
|
|
39
|
+
Secret values are never exposed in list operations for security.
|
|
40
|
+
Use get_secret with include_value=True to retrieve specific values.
|
|
41
|
+
"""
|
|
42
|
+
return list_secrets_via_graphql(
|
|
43
|
+
self.client,
|
|
44
|
+
location_name=location_name,
|
|
45
|
+
scope=scope,
|
|
46
|
+
include_values=False, # Never expose values in list operations
|
|
47
|
+
limit=limit,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def get_secret(
|
|
51
|
+
self,
|
|
52
|
+
secret_name: str,
|
|
53
|
+
location_name: str | None = None,
|
|
54
|
+
include_value: bool = False,
|
|
55
|
+
) -> "DgApiSecret":
|
|
56
|
+
"""Get a specific secret.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
secret_name: Name of the secret to retrieve
|
|
60
|
+
location_name: Optional filter by code location name
|
|
61
|
+
include_value: Whether to include the secret value (default: False for security)
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
DgApiSecret: Single secret with optional value
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
ValueError: If secret not found
|
|
68
|
+
"""
|
|
69
|
+
return get_secret_via_graphql(
|
|
70
|
+
self.client,
|
|
71
|
+
secret_name=secret_name,
|
|
72
|
+
location_name=location_name,
|
|
73
|
+
include_value=include_value,
|
|
74
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Sensor API implementation."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from dagster_dg_cli.api_layer.graphql_adapter.sensor import (
|
|
7
|
+
get_sensor_via_graphql,
|
|
8
|
+
list_sensors_via_graphql,
|
|
9
|
+
)
|
|
10
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from dagster_dg_cli.api_layer.schemas.sensor import DgApiSensor, DgApiSensorList
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class DgApiSensorApi:
|
|
18
|
+
"""API for sensor operations."""
|
|
19
|
+
|
|
20
|
+
client: IGraphQLClient
|
|
21
|
+
|
|
22
|
+
def list_sensors(
|
|
23
|
+
self,
|
|
24
|
+
repository_location_name: str | None = None,
|
|
25
|
+
repository_name: str | None = None,
|
|
26
|
+
) -> "DgApiSensorList":
|
|
27
|
+
"""List all sensors, optionally filtered by repository location and name."""
|
|
28
|
+
return list_sensors_via_graphql(
|
|
29
|
+
self.client,
|
|
30
|
+
repository_location_name=repository_location_name,
|
|
31
|
+
repository_name=repository_name,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def get_sensor(
|
|
35
|
+
self,
|
|
36
|
+
sensor_name: str,
|
|
37
|
+
repository_location_name: str,
|
|
38
|
+
repository_name: str,
|
|
39
|
+
) -> "DgApiSensor":
|
|
40
|
+
"""Get sensor by name and repository details."""
|
|
41
|
+
return get_sensor_via_graphql(
|
|
42
|
+
self.client,
|
|
43
|
+
sensor_name=sensor_name,
|
|
44
|
+
repository_location_name=repository_location_name,
|
|
45
|
+
repository_name=repository_name,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def get_sensor_by_name(self, sensor_name: str) -> "DgApiSensor":
|
|
49
|
+
"""Get sensor by name, searching across all repositories."""
|
|
50
|
+
from dagster_dg_cli.api_layer.graphql_adapter.sensor import get_sensor_by_name_via_graphql
|
|
51
|
+
|
|
52
|
+
return get_sensor_by_name_via_graphql(self.client, sensor_name=sensor_name)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""GraphQL adapter layer for Dagster Plus CLI."""
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""GraphQL implementation for agent operations."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
4
|
+
|
|
5
|
+
from dagster_dg_cli.utils.plus.gql_client import IGraphQLClient
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from dagster_dg_cli.api_layer.schemas.agent import DgApiAgent, DgApiAgentList
|
|
9
|
+
|
|
10
|
+
# GraphQL queries
|
|
11
|
+
LIST_AGENTS_QUERY = """
|
|
12
|
+
query ListAgents {
|
|
13
|
+
agents {
|
|
14
|
+
id
|
|
15
|
+
agentLabel
|
|
16
|
+
status
|
|
17
|
+
lastHeartbeatTime
|
|
18
|
+
metadata {
|
|
19
|
+
key
|
|
20
|
+
value
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Note: There's no single 'agent' query, so we use 'agents' and filter client-side
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def process_agents_response(graphql_response: dict[str, Any]) -> "DgApiAgentList":
|
|
30
|
+
"""Process GraphQL response into AgentList.
|
|
31
|
+
|
|
32
|
+
This is a pure function that can be easily tested without mocking GraphQL clients.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
graphql_response: Raw GraphQL response containing "agents"
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
AgentList: Processed agent data
|
|
39
|
+
"""
|
|
40
|
+
# Import pydantic models only when needed
|
|
41
|
+
from dagster_dg_cli.api_layer.schemas.agent import (
|
|
42
|
+
DgApiAgent,
|
|
43
|
+
DgApiAgentList,
|
|
44
|
+
DgApiAgentMetadataEntry,
|
|
45
|
+
DgApiAgentStatus,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
agents_data = graphql_response.get("agents", [])
|
|
49
|
+
|
|
50
|
+
agents = []
|
|
51
|
+
for a in agents_data:
|
|
52
|
+
metadata = [
|
|
53
|
+
DgApiAgentMetadataEntry(key=m["key"], value=m["value"]) for m in a.get("metadata", [])
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
agents.append(
|
|
57
|
+
DgApiAgent(
|
|
58
|
+
id=a["id"],
|
|
59
|
+
agent_label=a.get("agentLabel"),
|
|
60
|
+
status=DgApiAgentStatus[a["status"]],
|
|
61
|
+
last_heartbeat_time=a.get("lastHeartbeatTime"),
|
|
62
|
+
metadata=metadata,
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return DgApiAgentList(
|
|
67
|
+
items=agents,
|
|
68
|
+
total=len(agents),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def list_agents_via_graphql(
|
|
73
|
+
client: IGraphQLClient,
|
|
74
|
+
limit: int | None = None,
|
|
75
|
+
) -> "DgApiAgentList":
|
|
76
|
+
"""Fetch agents using GraphQL.
|
|
77
|
+
This is an implementation detail that can be replaced with REST calls later.
|
|
78
|
+
"""
|
|
79
|
+
result = client.execute(LIST_AGENTS_QUERY)
|
|
80
|
+
|
|
81
|
+
agent_list = process_agents_response(result)
|
|
82
|
+
|
|
83
|
+
# Apply limit if specified
|
|
84
|
+
if limit is not None:
|
|
85
|
+
agent_list.items = agent_list.items[:limit]
|
|
86
|
+
agent_list.total = len(agent_list.items)
|
|
87
|
+
|
|
88
|
+
return agent_list
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_agent_via_graphql(
|
|
92
|
+
client: IGraphQLClient,
|
|
93
|
+
agent_id: str,
|
|
94
|
+
) -> Optional["DgApiAgent"]:
|
|
95
|
+
"""Fetch single agent using GraphQL.
|
|
96
|
+
This is an implementation detail that can be replaced with REST calls later.
|
|
97
|
+
|
|
98
|
+
Note: Since there's no single 'agent' query, we fetch all agents and filter client-side.
|
|
99
|
+
"""
|
|
100
|
+
result = client.execute(LIST_AGENTS_QUERY)
|
|
101
|
+
agent_list = process_agents_response(result)
|
|
102
|
+
|
|
103
|
+
# Find the agent with matching ID
|
|
104
|
+
for agent in agent_list.items:
|
|
105
|
+
if agent.id == agent_id:
|
|
106
|
+
return agent
|
|
107
|
+
|
|
108
|
+
return None
|