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.
Files changed (130) hide show
  1. dagster_dg_cli/__init__.py +5 -0
  2. dagster_dg_cli/api_layer/__init__.py +1 -0
  3. dagster_dg_cli/api_layer/api/__init__.py +1 -0
  4. dagster_dg_cli/api_layer/api/agent.py +24 -0
  5. dagster_dg_cli/api_layer/api/asset.py +58 -0
  6. dagster_dg_cli/api_layer/api/deployments.py +24 -0
  7. dagster_dg_cli/api_layer/api/run.py +21 -0
  8. dagster_dg_cli/api_layer/api/run_event.py +76 -0
  9. dagster_dg_cli/api_layer/api/schedule.py +54 -0
  10. dagster_dg_cli/api_layer/api/secret.py +74 -0
  11. dagster_dg_cli/api_layer/api/sensor.py +52 -0
  12. dagster_dg_cli/api_layer/graphql_adapter/__init__.py +1 -0
  13. dagster_dg_cli/api_layer/graphql_adapter/agent.py +108 -0
  14. dagster_dg_cli/api_layer/graphql_adapter/asset.py +643 -0
  15. dagster_dg_cli/api_layer/graphql_adapter/deployment.py +145 -0
  16. dagster_dg_cli/api_layer/graphql_adapter/run.py +60 -0
  17. dagster_dg_cli/api_layer/graphql_adapter/run_event.py +197 -0
  18. dagster_dg_cli/api_layer/graphql_adapter/schedule.py +299 -0
  19. dagster_dg_cli/api_layer/graphql_adapter/secret.py +214 -0
  20. dagster_dg_cli/api_layer/graphql_adapter/sensor.py +287 -0
  21. dagster_dg_cli/api_layer/schemas/__init__.py +1 -0
  22. dagster_dg_cli/api_layer/schemas/agent.py +42 -0
  23. dagster_dg_cli/api_layer/schemas/asset.py +82 -0
  24. dagster_dg_cli/api_layer/schemas/deployment.py +30 -0
  25. dagster_dg_cli/api_layer/schemas/run.py +31 -0
  26. dagster_dg_cli/api_layer/schemas/run_event.py +61 -0
  27. dagster_dg_cli/api_layer/schemas/schedule.py +36 -0
  28. dagster_dg_cli/api_layer/schemas/secret.py +78 -0
  29. dagster_dg_cli/api_layer/schemas/sensor.py +45 -0
  30. dagster_dg_cli/cli/__init__.py +76 -0
  31. dagster_dg_cli/cli/__main__.py +4 -0
  32. dagster_dg_cli/cli/api/__init__.py +5 -0
  33. dagster_dg_cli/cli/api/agent.py +111 -0
  34. dagster_dg_cli/cli/api/asset.py +138 -0
  35. dagster_dg_cli/cli/api/cli_group.py +33 -0
  36. dagster_dg_cli/cli/api/client.py +86 -0
  37. dagster_dg_cli/cli/api/deployment.py +108 -0
  38. dagster_dg_cli/cli/api/formatters.py +362 -0
  39. dagster_dg_cli/cli/api/log.py +194 -0
  40. dagster_dg_cli/cli/api/run.py +88 -0
  41. dagster_dg_cli/cli/api/run_event.py +140 -0
  42. dagster_dg_cli/cli/api/schedule.py +192 -0
  43. dagster_dg_cli/cli/api/secret.py +155 -0
  44. dagster_dg_cli/cli/api/sensor.py +179 -0
  45. dagster_dg_cli/cli/api/shared.py +30 -0
  46. dagster_dg_cli/cli/check.py +219 -0
  47. dagster_dg_cli/cli/defs_state.py +170 -0
  48. dagster_dg_cli/cli/dev.py +174 -0
  49. dagster_dg_cli/cli/entrypoint.py +4 -0
  50. dagster_dg_cli/cli/launch.py +99 -0
  51. dagster_dg_cli/cli/list.py +592 -0
  52. dagster_dg_cli/cli/mcp_server.py +107 -0
  53. dagster_dg_cli/cli/plus/__init__.py +21 -0
  54. dagster_dg_cli/cli/plus/constants.py +19 -0
  55. dagster_dg_cli/cli/plus/create/__init__.py +14 -0
  56. dagster_dg_cli/cli/plus/create/ci_api_token.py +28 -0
  57. dagster_dg_cli/cli/plus/create/env.py +221 -0
  58. dagster_dg_cli/cli/plus/deploy/__init__.py +4 -0
  59. dagster_dg_cli/cli/plus/deploy/commands.py +721 -0
  60. dagster_dg_cli/cli/plus/deploy/configure/__init__.py +1 -0
  61. dagster_dg_cli/cli/plus/deploy/configure/commands.py +495 -0
  62. dagster_dg_cli/cli/plus/deploy/configure/configure_build_artifacts.py +145 -0
  63. dagster_dg_cli/cli/plus/deploy/configure/configure_ci.py +318 -0
  64. dagster_dg_cli/cli/plus/deploy/configure/utils.py +556 -0
  65. dagster_dg_cli/cli/plus/deploy/deploy_session.py +312 -0
  66. dagster_dg_cli/cli/plus/deploy/validation.py +69 -0
  67. dagster_dg_cli/cli/plus/login.py +78 -0
  68. dagster_dg_cli/cli/plus/pull/__init__.py +12 -0
  69. dagster_dg_cli/cli/plus/pull/env.py +86 -0
  70. dagster_dg_cli/cli/scaffold/__init__.py +23 -0
  71. dagster_dg_cli/cli/scaffold/branch/CLAUDE.md +241 -0
  72. dagster_dg_cli/cli/scaffold/branch/__init__.py +1 -0
  73. dagster_dg_cli/cli/scaffold/branch/ai.py +284 -0
  74. dagster_dg_cli/cli/scaffold/branch/claude/__init__.py +1 -0
  75. dagster_dg_cli/cli/scaffold/branch/claude/diagnostics.py +349 -0
  76. dagster_dg_cli/cli/scaffold/branch/claude/sdk_client.py +188 -0
  77. dagster_dg_cli/cli/scaffold/branch/command.py +439 -0
  78. dagster_dg_cli/cli/scaffold/branch/constants.py +63 -0
  79. dagster_dg_cli/cli/scaffold/branch/dg_scaffold_pr_sequencing_plan.md +262 -0
  80. dagster_dg_cli/cli/scaffold/branch/git.py +182 -0
  81. dagster_dg_cli/cli/scaffold/branch/models.py +76 -0
  82. dagster_dg_cli/cli/scaffold/branch/planning.py +382 -0
  83. dagster_dg_cli/cli/scaffold/branch/pr1_diagnostics_implementation_plan.md +123 -0
  84. dagster_dg_cli/cli/scaffold/branch/prompts/best_practices.md +70 -0
  85. dagster_dg_cli/cli/scaffold/branch/prompts/planning_prompt.md +50 -0
  86. dagster_dg_cli/cli/scaffold/branch/version_utils.py +9 -0
  87. dagster_dg_cli/cli/scaffold/build_artifacts.py +118 -0
  88. dagster_dg_cli/cli/scaffold/component.py +80 -0
  89. dagster_dg_cli/cli/scaffold/defs.py +408 -0
  90. dagster_dg_cli/cli/scaffold/github_actions.py +116 -0
  91. dagster_dg_cli/cli/utils.py +597 -0
  92. dagster_dg_cli/docs/.gitignore +9 -0
  93. dagster_dg_cli/mcp/__init__.py +0 -0
  94. dagster_dg_cli/mcp/server.py +308 -0
  95. dagster_dg_cli/py.typed +1 -0
  96. dagster_dg_cli/scaffold.py +135 -0
  97. dagster_dg_cli/templates/COMPONENT_TYPE/COMPONENT_TYPE_NAME_PLACEHOLDER.py.jinja +27 -0
  98. dagster_dg_cli/templates/build-location-fragment-gitlab.yaml +4 -0
  99. dagster_dg_cli/templates/build-location-fragment.yaml +16 -0
  100. dagster_dg_cli/templates/deploy_uv_Dockerfile.jinja +40 -0
  101. dagster_dg_cli/templates/deploy_uv_editable_Dockerfile.jinja +49 -0
  102. dagster_dg_cli/templates/hybrid-github-action.yaml +126 -0
  103. dagster_dg_cli/templates/hybrid-gitlab-ci.yaml +77 -0
  104. dagster_dg_cli/templates/registry_fragments/github/azure-container-registry-login-fragment.yaml +9 -0
  105. dagster_dg_cli/templates/registry_fragments/github/dockerhub-login-fragment.yaml +8 -0
  106. dagster_dg_cli/templates/registry_fragments/github/ecr-login-fragment.yaml +12 -0
  107. dagster_dg_cli/templates/registry_fragments/github/gcr-login-fragment.yaml +9 -0
  108. dagster_dg_cli/templates/registry_fragments/github/github-container-registry-login-fragment.yaml +9 -0
  109. dagster_dg_cli/templates/registry_fragments/gitlab/azure-container-registry-login-fragment.yaml +2 -0
  110. dagster_dg_cli/templates/registry_fragments/gitlab/dockerhub-login-fragment.yaml +2 -0
  111. dagster_dg_cli/templates/registry_fragments/gitlab/ecr-login-fragment.yaml +6 -0
  112. dagster_dg_cli/templates/registry_fragments/gitlab/gcr-login-fragment.yaml +2 -0
  113. dagster_dg_cli/templates/registry_fragments/gitlab/github-container-registry-login-fragment.yaml +2 -0
  114. dagster_dg_cli/templates/serverless-github-action.yaml +126 -0
  115. dagster_dg_cli/templates/serverless-gitlab-ci.yaml +64 -0
  116. dagster_dg_cli/utils/__init__.py +0 -0
  117. dagster_dg_cli/utils/claude_utils.py +15 -0
  118. dagster_dg_cli/utils/plus/__init__.py +0 -0
  119. dagster_dg_cli/utils/plus/build.py +98 -0
  120. dagster_dg_cli/utils/plus/defs_state_storage.py +174 -0
  121. dagster_dg_cli/utils/plus/gql.py +182 -0
  122. dagster_dg_cli/utils/plus/gql_client.py +100 -0
  123. dagster_dg_cli/utils/ui.py +87 -0
  124. dagster_dg_cli/utils/yaml_template_generator.py +419 -0
  125. dagster_dg_cli/version.py +1 -0
  126. dagster_dg_cli-1.12.16.dist-info/METADATA +23 -0
  127. dagster_dg_cli-1.12.16.dist-info/RECORD +130 -0
  128. dagster_dg_cli-1.12.16.dist-info/WHEEL +5 -0
  129. dagster_dg_cli-1.12.16.dist-info/entry_points.txt +2 -0
  130. dagster_dg_cli-1.12.16.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,5 @@
1
+ from dagster_shared.libraries import DagsterLibraryRegistry
2
+
3
+ from dagster_dg_cli.version import __version__ as __version__
4
+
5
+ DagsterLibraryRegistry.register("dagster-dg-cli", __version__)
@@ -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