dnastack-client-library 3.1.168__py3-none-any.whl → 3.1.170__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.
- dnastack/cli/commands/workbench/workflows/utils.py +97 -0
- dnastack/cli/commands/workbench/workflows/versions/__init__.py +2 -0
- dnastack/cli/commands/workbench/workflows/versions/dependencies/__init__.py +3 -0
- dnastack/cli/commands/workbench/workflows/versions/dependencies/commands.py +385 -0
- dnastack/client/workbench/ewes/models.py +2 -2
- dnastack/client/workbench/workflow/client.py +83 -1
- dnastack/client/workbench/workflow/models.py +38 -0
- dnastack/constants.py +1 -1
- {dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/METADATA +1 -1
- {dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/RECORD +14 -12
- {dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/WHEEL +0 -0
- {dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/entry_points.txt +0 -0
- {dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/licenses/LICENSE +0 -0
- {dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/top_level.txt +0 -0
|
@@ -215,3 +215,100 @@ class JavaScriptFunctionExtractor:
|
|
|
215
215
|
if match:
|
|
216
216
|
return match.group(1)
|
|
217
217
|
return None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class WorkflowDependencyParseError(Exception):
|
|
221
|
+
def __init__(self, message: str):
|
|
222
|
+
super().__init__(message)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def parse_workflow_dependency(dependency_str: str) -> tuple[str, Optional[str]]:
|
|
226
|
+
"""
|
|
227
|
+
Parse a workflow dependency string in the format 'workflow-id/version-id' or 'workflow-id'.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
dependency_str: The dependency string to parse
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
A tuple of (workflow_id, version_id) where version_id may be None
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
WorkflowDependencyParseError: If the dependency string is invalid
|
|
237
|
+
"""
|
|
238
|
+
if not dependency_str or not dependency_str.strip():
|
|
239
|
+
raise WorkflowDependencyParseError("Dependency string cannot be empty")
|
|
240
|
+
|
|
241
|
+
dependency_str = dependency_str.strip()
|
|
242
|
+
|
|
243
|
+
# Check for invalid characters or patterns
|
|
244
|
+
if dependency_str.startswith('/') or dependency_str.endswith('/'):
|
|
245
|
+
raise WorkflowDependencyParseError("Dependency string cannot start or end with '/'")
|
|
246
|
+
|
|
247
|
+
if '//' in dependency_str:
|
|
248
|
+
raise WorkflowDependencyParseError("Dependency string cannot contain consecutive '/' characters")
|
|
249
|
+
|
|
250
|
+
parts = dependency_str.split('/')
|
|
251
|
+
|
|
252
|
+
if len(parts) == 1:
|
|
253
|
+
# Only workflow ID provided
|
|
254
|
+
workflow_id = parts[0]
|
|
255
|
+
if not workflow_id:
|
|
256
|
+
raise WorkflowDependencyParseError("Workflow ID cannot be empty")
|
|
257
|
+
return workflow_id, None
|
|
258
|
+
elif len(parts) == 2:
|
|
259
|
+
# Both workflow ID and version ID provided
|
|
260
|
+
workflow_id, version_id = parts
|
|
261
|
+
if not workflow_id:
|
|
262
|
+
raise WorkflowDependencyParseError("Workflow ID cannot be empty")
|
|
263
|
+
if not version_id:
|
|
264
|
+
raise WorkflowDependencyParseError("Version ID cannot be empty when specified")
|
|
265
|
+
return workflow_id, version_id
|
|
266
|
+
else:
|
|
267
|
+
raise WorkflowDependencyParseError("Invalid dependency format. Expected 'workflow-id' or 'workflow-id/version-id'")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def get_latest_workflow_version(workflow_client: WorkflowClient, workflow_id: str) -> str:
|
|
271
|
+
"""
|
|
272
|
+
Get the latest version ID for a workflow.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
workflow_client: The workflow client to use
|
|
276
|
+
workflow_id: The workflow ID to get the latest version for
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
The latest version ID
|
|
280
|
+
|
|
281
|
+
Raises:
|
|
282
|
+
Exception: If the workflow is not found or has no versions
|
|
283
|
+
"""
|
|
284
|
+
try:
|
|
285
|
+
workflow = workflow_client.get_workflow(workflow_id)
|
|
286
|
+
if not workflow.latestVersion:
|
|
287
|
+
raise Exception(f"Workflow {workflow_id} has no versions")
|
|
288
|
+
return workflow.latestVersion
|
|
289
|
+
except Exception as e:
|
|
290
|
+
raise Exception(f"Failed to get latest version for workflow {workflow_id}: {str(e)}")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def resolve_workflow_dependency(workflow_client: WorkflowClient, dependency_str: str) -> tuple[str, str]:
|
|
294
|
+
"""
|
|
295
|
+
Resolve a workflow dependency string to a (workflow_id, version_id) tuple.
|
|
296
|
+
If version_id is not provided, fetches the latest version.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
workflow_client: The workflow client to use
|
|
300
|
+
dependency_str: The dependency string to resolve
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
A tuple of (workflow_id, version_id)
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
WorkflowDependencyParseError: If the dependency string is invalid
|
|
307
|
+
Exception: If the workflow is not found or has no versions
|
|
308
|
+
"""
|
|
309
|
+
workflow_id, version_id = parse_workflow_dependency(dependency_str)
|
|
310
|
+
|
|
311
|
+
if version_id is None:
|
|
312
|
+
version_id = get_latest_workflow_version(workflow_client, workflow_id)
|
|
313
|
+
|
|
314
|
+
return workflow_id, version_id
|
|
@@ -2,6 +2,7 @@ from dnastack.cli.commands.workbench.workflows.versions.commands import init_wor
|
|
|
2
2
|
from dnastack.cli.commands.workbench.workflows.versions.defaults import workflows_versions_defaults_command_group
|
|
3
3
|
from dnastack.cli.commands.workbench.workflows.versions.transformations import \
|
|
4
4
|
workflows_versions_transformations_command_group
|
|
5
|
+
from dnastack.cli.commands.workbench.workflows.versions.dependencies import dependencies
|
|
5
6
|
from dnastack.cli.core.group import formatted_group
|
|
6
7
|
|
|
7
8
|
|
|
@@ -15,3 +16,4 @@ init_workflows_versions_commands(workflows_versions_command_group)
|
|
|
15
16
|
# Register sub-groups
|
|
16
17
|
workflows_versions_command_group.add_command(workflows_versions_defaults_command_group)
|
|
17
18
|
workflows_versions_command_group.add_command(workflows_versions_transformations_command_group)
|
|
19
|
+
workflows_versions_command_group.add_command(dependencies)
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from dnastack.cli.commands.utils import MAX_RESULTS_ARG, PAGINATION_PAGE_ARG, PAGINATION_PAGE_SIZE_ARG
|
|
7
|
+
from dnastack.cli.commands.workbench.utils import NAMESPACE_ARG
|
|
8
|
+
from dnastack.cli.commands.workbench.workflows.utils import (
|
|
9
|
+
get_workflow_client,
|
|
10
|
+
resolve_workflow_dependency,
|
|
11
|
+
WorkflowDependencyParseError
|
|
12
|
+
)
|
|
13
|
+
from dnastack.cli.core.command import formatted_command
|
|
14
|
+
from dnastack.cli.core.command_spec import ArgumentSpec, ArgumentType, CONTEXT_ARG, SINGLE_ENDPOINT_ID_ARG
|
|
15
|
+
from dnastack.cli.core.group import formatted_group
|
|
16
|
+
from dnastack.cli.helpers.iterator_printer import show_iterator, OutputFormat
|
|
17
|
+
from dnastack.client.workbench.workflow.models import (
|
|
18
|
+
WorkflowDependencyCreateRequest,
|
|
19
|
+
WorkflowDependencyUpdateRequest,
|
|
20
|
+
WorkflowDependencyPrerequisite,
|
|
21
|
+
WorkflowDependencyListOptions
|
|
22
|
+
)
|
|
23
|
+
from dnastack.common.logger import get_logger
|
|
24
|
+
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resolve_dependencies(workflow_client, dependency_strings: List[str]) -> List[WorkflowDependencyPrerequisite]:
|
|
29
|
+
"""
|
|
30
|
+
Parse and resolve a list of dependency strings into WorkflowDependencyPrerequisite objects.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
workflow_client: The workflow client for resolving latest versions
|
|
34
|
+
dependency_strings: List of dependency strings in format "workflow-id/version-id" or "workflow-id"
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of resolved WorkflowDependencyPrerequisite objects
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
click.ClickException: If any dependency string is invalid or cannot be resolved
|
|
41
|
+
"""
|
|
42
|
+
resolved_dependencies = []
|
|
43
|
+
for dep_str in dependency_strings:
|
|
44
|
+
try:
|
|
45
|
+
dep_workflow_id, dep_version_id = resolve_workflow_dependency(workflow_client, dep_str)
|
|
46
|
+
resolved_dependencies.append(WorkflowDependencyPrerequisite(
|
|
47
|
+
workflow_id=dep_workflow_id,
|
|
48
|
+
workflow_version_id=dep_version_id
|
|
49
|
+
))
|
|
50
|
+
except WorkflowDependencyParseError as e:
|
|
51
|
+
raise click.ClickException(f"Invalid dependency format '{dep_str}': {e}")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
raise click.ClickException(f"Error resolving dependency '{dep_str}': {e}")
|
|
54
|
+
|
|
55
|
+
return resolved_dependencies
|
|
56
|
+
|
|
57
|
+
WORKFLOW_ID_ARG = ArgumentSpec(
|
|
58
|
+
name='workflow_id',
|
|
59
|
+
arg_names=['--workflow'],
|
|
60
|
+
help='The workflow ID',
|
|
61
|
+
required=True
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
VERSION_ID_ARG = ArgumentSpec(
|
|
65
|
+
name='version_id',
|
|
66
|
+
arg_names=['--version'],
|
|
67
|
+
help='The workflow version ID',
|
|
68
|
+
required=True
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
DEPENDENCY_NAME_ARG = ArgumentSpec(
|
|
72
|
+
name='name',
|
|
73
|
+
arg_names=['--name'],
|
|
74
|
+
help='The dependency name',
|
|
75
|
+
required=True
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
DEPENDENCY_ARG = ArgumentSpec(
|
|
79
|
+
name='dependencies',
|
|
80
|
+
arg_names=['--dependency'],
|
|
81
|
+
help='Workflow dependency in format "workflow-id/version-id" or "workflow-id" (can be specified multiple times)',
|
|
82
|
+
multiple=True,
|
|
83
|
+
required=True
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
GLOBAL_ARG = ArgumentSpec(
|
|
87
|
+
name='global_action',
|
|
88
|
+
arg_names=['--global'],
|
|
89
|
+
help='Create a global dependency (admin only)',
|
|
90
|
+
type=bool
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
FORCE_ARG = ArgumentSpec(
|
|
94
|
+
name='force',
|
|
95
|
+
arg_names=['--force'],
|
|
96
|
+
help='Skip confirmation prompt',
|
|
97
|
+
type=bool
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@formatted_group(name='dependencies')
|
|
102
|
+
def dependencies():
|
|
103
|
+
"""Manage workflow version dependencies"""
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@formatted_command(
|
|
108
|
+
group=dependencies,
|
|
109
|
+
name='create',
|
|
110
|
+
specs=[
|
|
111
|
+
CONTEXT_ARG,
|
|
112
|
+
NAMESPACE_ARG,
|
|
113
|
+
SINGLE_ENDPOINT_ID_ARG,
|
|
114
|
+
WORKFLOW_ID_ARG,
|
|
115
|
+
VERSION_ID_ARG,
|
|
116
|
+
DEPENDENCY_NAME_ARG,
|
|
117
|
+
DEPENDENCY_ARG,
|
|
118
|
+
GLOBAL_ARG
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
def create(context: Optional[str] = None,
|
|
122
|
+
namespace: Optional[str] = None,
|
|
123
|
+
endpoint_id: Optional[str] = None,
|
|
124
|
+
workflow_id: str = None,
|
|
125
|
+
version_id: str = None,
|
|
126
|
+
name: str = None,
|
|
127
|
+
dependencies: List[str] = None,
|
|
128
|
+
global_action: bool = False):
|
|
129
|
+
"""Create a new workflow version dependency"""
|
|
130
|
+
|
|
131
|
+
workflow_client = get_workflow_client(
|
|
132
|
+
context_name=context,
|
|
133
|
+
endpoint_id=endpoint_id,
|
|
134
|
+
namespace=namespace
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Parse and resolve dependencies
|
|
138
|
+
resolved_dependencies = _resolve_dependencies(workflow_client, dependencies)
|
|
139
|
+
|
|
140
|
+
# Create the dependency request
|
|
141
|
+
create_request = WorkflowDependencyCreateRequest(
|
|
142
|
+
name=name,
|
|
143
|
+
dependencies=resolved_dependencies
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Create the dependency
|
|
147
|
+
dependency = workflow_client.create_workflow_dependency(
|
|
148
|
+
workflow_id=workflow_id,
|
|
149
|
+
workflow_version_id=version_id,
|
|
150
|
+
workflow_dependency_create_request=create_request,
|
|
151
|
+
admin_only_action=global_action
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
click.echo(json.dumps(dependency.dict(), indent=2))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@formatted_command(
|
|
158
|
+
group=dependencies,
|
|
159
|
+
name='list',
|
|
160
|
+
specs=[
|
|
161
|
+
CONTEXT_ARG,
|
|
162
|
+
NAMESPACE_ARG,
|
|
163
|
+
SINGLE_ENDPOINT_ID_ARG,
|
|
164
|
+
WORKFLOW_ID_ARG,
|
|
165
|
+
VERSION_ID_ARG,
|
|
166
|
+
MAX_RESULTS_ARG,
|
|
167
|
+
PAGINATION_PAGE_ARG,
|
|
168
|
+
PAGINATION_PAGE_SIZE_ARG
|
|
169
|
+
]
|
|
170
|
+
)
|
|
171
|
+
def list(context: Optional[str] = None,
|
|
172
|
+
namespace: Optional[str] = None,
|
|
173
|
+
endpoint_id: Optional[str] = None,
|
|
174
|
+
workflow_id: str = None,
|
|
175
|
+
version_id: str = None,
|
|
176
|
+
max_results: Optional[int] = None,
|
|
177
|
+
page: Optional[int] = None,
|
|
178
|
+
page_size: Optional[int] = None):
|
|
179
|
+
"""List workflow version dependencies"""
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
workflow_client = get_workflow_client(
|
|
183
|
+
context_name=context,
|
|
184
|
+
endpoint_id=endpoint_id,
|
|
185
|
+
namespace=namespace
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Set up list options
|
|
189
|
+
list_options = WorkflowDependencyListOptions()
|
|
190
|
+
if page is not None:
|
|
191
|
+
list_options.page = page
|
|
192
|
+
if page_size is not None:
|
|
193
|
+
list_options.page_size = page_size
|
|
194
|
+
|
|
195
|
+
# Get dependencies
|
|
196
|
+
dependencies_iterator = workflow_client.list_workflow_dependencies(
|
|
197
|
+
workflow_id=workflow_id,
|
|
198
|
+
workflow_version_id=version_id,
|
|
199
|
+
list_options=list_options,
|
|
200
|
+
max_results=max_results
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
show_iterator(output_format=OutputFormat.JSON, iterator=dependencies_iterator)
|
|
204
|
+
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(f"Failed to list workflow dependencies: {e}")
|
|
207
|
+
raise click.ClickException(f"Failed to list workflow dependencies: {e}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@formatted_command(
|
|
211
|
+
group=dependencies,
|
|
212
|
+
name='describe',
|
|
213
|
+
specs=[
|
|
214
|
+
CONTEXT_ARG,
|
|
215
|
+
NAMESPACE_ARG,
|
|
216
|
+
SINGLE_ENDPOINT_ID_ARG,
|
|
217
|
+
WORKFLOW_ID_ARG,
|
|
218
|
+
VERSION_ID_ARG,
|
|
219
|
+
ArgumentSpec(
|
|
220
|
+
name='dependency_ids',
|
|
221
|
+
arg_type=ArgumentType.POSITIONAL,
|
|
222
|
+
help='The dependency IDs to describe',
|
|
223
|
+
multiple=True,
|
|
224
|
+
required=True
|
|
225
|
+
)
|
|
226
|
+
]
|
|
227
|
+
)
|
|
228
|
+
def describe(context: Optional[str] = None,
|
|
229
|
+
namespace: Optional[str] = None,
|
|
230
|
+
endpoint_id: Optional[str] = None,
|
|
231
|
+
workflow_id: str = None,
|
|
232
|
+
version_id: str = None,
|
|
233
|
+
dependency_ids: List[str] = None):
|
|
234
|
+
"""Describe workflow version dependencies"""
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
workflow_client = get_workflow_client(
|
|
238
|
+
context_name=context,
|
|
239
|
+
endpoint_id=endpoint_id,
|
|
240
|
+
namespace=namespace
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Get each dependency
|
|
244
|
+
dependencies = []
|
|
245
|
+
for dependency_id in dependency_ids:
|
|
246
|
+
try:
|
|
247
|
+
dependency = workflow_client.get_workflow_dependency(
|
|
248
|
+
workflow_id=workflow_id,
|
|
249
|
+
workflow_version_id=version_id,
|
|
250
|
+
dependency_id=dependency_id
|
|
251
|
+
)
|
|
252
|
+
dependencies.append(dependency.dict())
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.error(f"Failed to get dependency {dependency_id}: {e}")
|
|
255
|
+
raise click.ClickException(f"Failed to get dependency {dependency_id}: {e}")
|
|
256
|
+
|
|
257
|
+
# Output as JSON array
|
|
258
|
+
click.echo(json.dumps(dependencies, indent=2))
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
logger.error(f"Failed to describe workflow dependencies: {e}")
|
|
262
|
+
raise click.ClickException(f"Failed to describe workflow dependencies: {e}")
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@formatted_command(
|
|
266
|
+
group=dependencies,
|
|
267
|
+
name='update',
|
|
268
|
+
specs=[
|
|
269
|
+
CONTEXT_ARG,
|
|
270
|
+
NAMESPACE_ARG,
|
|
271
|
+
SINGLE_ENDPOINT_ID_ARG,
|
|
272
|
+
WORKFLOW_ID_ARG,
|
|
273
|
+
VERSION_ID_ARG,
|
|
274
|
+
DEPENDENCY_NAME_ARG,
|
|
275
|
+
DEPENDENCY_ARG,
|
|
276
|
+
GLOBAL_ARG,
|
|
277
|
+
ArgumentSpec(
|
|
278
|
+
name='dependency_id',
|
|
279
|
+
arg_type=ArgumentType.POSITIONAL,
|
|
280
|
+
help='The dependency ID to update',
|
|
281
|
+
required=True
|
|
282
|
+
)
|
|
283
|
+
]
|
|
284
|
+
)
|
|
285
|
+
def update(context: Optional[str] = None,
|
|
286
|
+
namespace: Optional[str] = None,
|
|
287
|
+
endpoint_id: Optional[str] = None,
|
|
288
|
+
workflow_id: str = None,
|
|
289
|
+
version_id: str = None,
|
|
290
|
+
name: str = None,
|
|
291
|
+
dependencies: List[str] = None,
|
|
292
|
+
global_action: bool = False,
|
|
293
|
+
dependency_id: str = None):
|
|
294
|
+
"""Update a workflow version dependency"""
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
workflow_client = get_workflow_client(
|
|
298
|
+
context_name=context,
|
|
299
|
+
endpoint_id=endpoint_id,
|
|
300
|
+
namespace=namespace
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Parse and resolve dependencies
|
|
304
|
+
resolved_dependencies = _resolve_dependencies(workflow_client, dependencies)
|
|
305
|
+
|
|
306
|
+
# Create the update request
|
|
307
|
+
update_request = WorkflowDependencyUpdateRequest(
|
|
308
|
+
name=name,
|
|
309
|
+
dependencies=resolved_dependencies
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Update the dependency
|
|
313
|
+
dependency = workflow_client.update_workflow_dependency(
|
|
314
|
+
workflow_id=workflow_id,
|
|
315
|
+
workflow_version_id=version_id,
|
|
316
|
+
dependency_id=dependency_id,
|
|
317
|
+
workflow_dependency_update_request=update_request,
|
|
318
|
+
admin_only_action=global_action
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
click.echo(json.dumps(dependency.dict(), indent=2))
|
|
322
|
+
|
|
323
|
+
except Exception as e:
|
|
324
|
+
logger.error(f"Failed to update workflow dependency: {e}")
|
|
325
|
+
raise click.ClickException(f"Failed to update workflow dependency: {e}")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@formatted_command(
|
|
329
|
+
group=dependencies,
|
|
330
|
+
name='delete',
|
|
331
|
+
specs=[
|
|
332
|
+
CONTEXT_ARG,
|
|
333
|
+
NAMESPACE_ARG,
|
|
334
|
+
SINGLE_ENDPOINT_ID_ARG,
|
|
335
|
+
WORKFLOW_ID_ARG,
|
|
336
|
+
VERSION_ID_ARG,
|
|
337
|
+
GLOBAL_ARG,
|
|
338
|
+
FORCE_ARG,
|
|
339
|
+
ArgumentSpec(
|
|
340
|
+
name='dependency_id',
|
|
341
|
+
arg_type=ArgumentType.POSITIONAL,
|
|
342
|
+
help='The dependency ID to delete',
|
|
343
|
+
required=True
|
|
344
|
+
)
|
|
345
|
+
]
|
|
346
|
+
)
|
|
347
|
+
def delete(context: Optional[str] = None,
|
|
348
|
+
namespace: Optional[str] = None,
|
|
349
|
+
endpoint_id: Optional[str] = None,
|
|
350
|
+
workflow_id: str = None,
|
|
351
|
+
version_id: str = None,
|
|
352
|
+
global_action: bool = False,
|
|
353
|
+
force: bool = False,
|
|
354
|
+
dependency_id: str = None):
|
|
355
|
+
"""Delete a workflow version dependency"""
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
workflow_client = get_workflow_client(
|
|
359
|
+
context_name=context,
|
|
360
|
+
endpoint_id=endpoint_id,
|
|
361
|
+
namespace=namespace
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Confirm deletion unless --force is used
|
|
365
|
+
if not force:
|
|
366
|
+
if not click.confirm(
|
|
367
|
+
f"Are you sure you want to delete dependency '{dependency_id}' "
|
|
368
|
+
f"for workflow '{workflow_id}' version '{version_id}'?"
|
|
369
|
+
):
|
|
370
|
+
click.echo("Deletion cancelled.")
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
# Delete the dependency
|
|
374
|
+
workflow_client.delete_workflow_dependency(
|
|
375
|
+
workflow_id=workflow_id,
|
|
376
|
+
workflow_version_id=version_id,
|
|
377
|
+
dependency_id=dependency_id,
|
|
378
|
+
admin_only_action=global_action
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
click.echo(f"Dependency '{dependency_id}' deleted successfully.")
|
|
382
|
+
|
|
383
|
+
except Exception as e:
|
|
384
|
+
logger.error(f"Failed to delete workflow dependency: {e}")
|
|
385
|
+
raise click.ClickException(f"Failed to delete workflow dependency: {e}")
|
|
@@ -101,7 +101,7 @@ class ExtendedRunRequest(BaseModel):
|
|
|
101
101
|
submitted_by: Optional[str]
|
|
102
102
|
workflow_params: Optional[Dict]
|
|
103
103
|
workflow_engine_parameters: Optional[Dict]
|
|
104
|
-
dependencies: Optional[
|
|
104
|
+
dependencies: Optional[List[RunDependency]]
|
|
105
105
|
tags: Optional[Dict]
|
|
106
106
|
|
|
107
107
|
|
|
@@ -141,7 +141,7 @@ class ExtendedRun(BaseModel):
|
|
|
141
141
|
task_logs: Optional[List[Log]]
|
|
142
142
|
task_logs_url: Optional[str]
|
|
143
143
|
outputs: Optional[Dict]
|
|
144
|
-
dependencies: Optional[
|
|
144
|
+
dependencies: Optional[List[RunDependency]]
|
|
145
145
|
events: Optional[List[RunEvent]]
|
|
146
146
|
|
|
147
147
|
|
|
@@ -12,7 +12,8 @@ from dnastack.client.workbench.workflow.models import WorkflowDescriptor, Workfl
|
|
|
12
12
|
WorkflowVersionListOptions, WorkflowFile, WorkflowDefaultsListResponse, WorkflowDefaultsListOptions, \
|
|
13
13
|
WorkflowTransformationListOptions, WorkflowTransformationListResponse, WorkflowDefaults, WorkflowDefaultsSelector, \
|
|
14
14
|
ResolvedWorkflow, WorkflowDefaultsUpdateRequest, WorkflowTransformation, WorkflowTransformationCreate, \
|
|
15
|
-
WorkflowDefaultsCreateRequest
|
|
15
|
+
WorkflowDefaultsCreateRequest, WorkflowDependency, WorkflowDependencyListResponse, WorkflowDependencyListOptions, \
|
|
16
|
+
WorkflowDependencyCreateRequest, WorkflowDependencyUpdateRequest
|
|
16
17
|
from dnastack.common.tracing import Span
|
|
17
18
|
from dnastack.http.session import JsonPatch, HttpSession
|
|
18
19
|
|
|
@@ -94,6 +95,26 @@ class WorkflowVersionsListResultLoader(WorkbenchResultLoader):
|
|
|
94
95
|
return WorkflowVersionListResponse(**response_body)
|
|
95
96
|
|
|
96
97
|
|
|
98
|
+
class WorkflowDependencyListResultLoader(WorkbenchResultLoader):
|
|
99
|
+
def __init__(self,
|
|
100
|
+
service_url: str,
|
|
101
|
+
http_session: HttpSession,
|
|
102
|
+
trace: Optional[Span],
|
|
103
|
+
list_options: Optional[WorkflowDependencyListOptions] = None,
|
|
104
|
+
max_results: int = None):
|
|
105
|
+
super().__init__(service_url=service_url,
|
|
106
|
+
http_session=http_session,
|
|
107
|
+
list_options=list_options,
|
|
108
|
+
max_results=max_results,
|
|
109
|
+
trace=trace)
|
|
110
|
+
|
|
111
|
+
def get_new_list_options(self) -> WorkflowDependencyListOptions:
|
|
112
|
+
return WorkflowDependencyListOptions()
|
|
113
|
+
|
|
114
|
+
def extract_api_response(self, response_body: dict) -> WorkflowDependencyListResponse:
|
|
115
|
+
return WorkflowDependencyListResponse(**response_body)
|
|
116
|
+
|
|
117
|
+
|
|
97
118
|
class WorkflowClient(BaseWorkbenchClient):
|
|
98
119
|
|
|
99
120
|
@staticmethod
|
|
@@ -359,3 +380,64 @@ class WorkflowClient(BaseWorkbenchClient):
|
|
|
359
380
|
json=workflow_transformation_create_request.dict()
|
|
360
381
|
)
|
|
361
382
|
return WorkflowTransformation(**response.json())
|
|
383
|
+
|
|
384
|
+
def list_workflow_dependencies(self, workflow_id: str, workflow_version_id: str,
|
|
385
|
+
list_options: Optional[WorkflowDependencyListOptions] = None,
|
|
386
|
+
max_results: int = None) -> Iterator[WorkflowDependency]:
|
|
387
|
+
return ResultIterator(WorkflowDependencyListResultLoader(
|
|
388
|
+
service_url=urljoin(self.endpoint.url, f'{self.namespace}/workflows/{workflow_id}/versions/{workflow_version_id}/dependencies'),
|
|
389
|
+
http_session=self.create_http_session(),
|
|
390
|
+
list_options=list_options,
|
|
391
|
+
max_results=max_results,
|
|
392
|
+
trace=None
|
|
393
|
+
))
|
|
394
|
+
|
|
395
|
+
def get_workflow_dependency(self, workflow_id: str, workflow_version_id: str, dependency_id: str,
|
|
396
|
+
trace: Optional[Span] = None) -> WorkflowDependency:
|
|
397
|
+
trace = trace or Span(origin=self)
|
|
398
|
+
with self.create_http_session() as session:
|
|
399
|
+
response = session.get(urljoin(self.endpoint.url, f'{self.namespace}/workflows/{workflow_id}/versions/{workflow_version_id}/dependencies/{dependency_id}'),
|
|
400
|
+
trace_context=trace)
|
|
401
|
+
return WorkflowDependency(**response.json())
|
|
402
|
+
|
|
403
|
+
def create_workflow_dependency(self, workflow_id: str, workflow_version_id: str,
|
|
404
|
+
workflow_dependency_create_request: WorkflowDependencyCreateRequest,
|
|
405
|
+
admin_only_action: bool = False) -> WorkflowDependency:
|
|
406
|
+
headers = {}
|
|
407
|
+
if admin_only_action:
|
|
408
|
+
headers['X-Admin-Only-Action'] = 'true'
|
|
409
|
+
|
|
410
|
+
with self.create_http_session() as session:
|
|
411
|
+
response = session.post(
|
|
412
|
+
urljoin(self.endpoint.url, f'{self.namespace}/workflows/{workflow_id}/versions/{workflow_version_id}/dependencies'),
|
|
413
|
+
json=workflow_dependency_create_request.dict(),
|
|
414
|
+
headers=headers
|
|
415
|
+
)
|
|
416
|
+
return WorkflowDependency(**response.json())
|
|
417
|
+
|
|
418
|
+
def update_workflow_dependency(self, workflow_id: str, workflow_version_id: str, dependency_id: str,
|
|
419
|
+
workflow_dependency_update_request: WorkflowDependencyUpdateRequest,
|
|
420
|
+
admin_only_action: bool = False) -> WorkflowDependency:
|
|
421
|
+
headers = {}
|
|
422
|
+
if admin_only_action:
|
|
423
|
+
headers['X-Admin-Only-Action'] = 'true'
|
|
424
|
+
|
|
425
|
+
with self.create_http_session() as session:
|
|
426
|
+
response = session.submit("PUT",
|
|
427
|
+
urljoin(self.endpoint.url, f'{self.namespace}/workflows/{workflow_id}/versions/{workflow_version_id}/dependencies/{dependency_id}'),
|
|
428
|
+
json=workflow_dependency_update_request.dict(),
|
|
429
|
+
headers=headers
|
|
430
|
+
)
|
|
431
|
+
return WorkflowDependency(**response.json())
|
|
432
|
+
|
|
433
|
+
def delete_workflow_dependency(self, workflow_id: str, workflow_version_id: str, dependency_id: str,
|
|
434
|
+
admin_only_action: bool = False):
|
|
435
|
+
headers = {}
|
|
436
|
+
if admin_only_action:
|
|
437
|
+
headers['X-Admin-Only-Action'] = 'true'
|
|
438
|
+
|
|
439
|
+
with self.create_http_session() as session:
|
|
440
|
+
session.delete(
|
|
441
|
+
urljoin(self.endpoint.url, f'{self.namespace}/workflows/{workflow_id}/versions/{workflow_version_id}/dependencies/{dependency_id}'),
|
|
442
|
+
headers=headers
|
|
443
|
+
)
|
|
@@ -186,3 +186,41 @@ class WorkflowTransformationListResponse(PaginatedResource):
|
|
|
186
186
|
|
|
187
187
|
def items(self) -> List[Any]:
|
|
188
188
|
return self.transformations
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class WorkflowDependencyPrerequisite(BaseModel):
|
|
192
|
+
workflow_id: Optional[str]
|
|
193
|
+
workflow_version_id: Optional[str]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class WorkflowDependency(BaseModel):
|
|
197
|
+
namespace: Optional[str]
|
|
198
|
+
id: Optional[str]
|
|
199
|
+
workflow_id: Optional[str]
|
|
200
|
+
workflow_version_id: Optional[str]
|
|
201
|
+
name: Optional[str]
|
|
202
|
+
dependencies: Optional[List[WorkflowDependencyPrerequisite]] = []
|
|
203
|
+
global_: Optional[bool] = Field(default=None, alias="global")
|
|
204
|
+
created_at: Optional[str]
|
|
205
|
+
updated_at: Optional[str]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class WorkflowDependencyCreateRequest(BaseModel):
|
|
209
|
+
name: str
|
|
210
|
+
dependencies: List[WorkflowDependencyPrerequisite] = []
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class WorkflowDependencyUpdateRequest(BaseModel):
|
|
214
|
+
name: str
|
|
215
|
+
dependencies: List[WorkflowDependencyPrerequisite] = []
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class WorkflowDependencyListOptions(BaseListOptions):
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class WorkflowDependencyListResponse(PaginatedResource):
|
|
223
|
+
dependencies: Optional[List[WorkflowDependency]] = []
|
|
224
|
+
|
|
225
|
+
def items(self) -> List[WorkflowDependency]:
|
|
226
|
+
return self.dependencies or []
|
dnastack/constants.py
CHANGED
{dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/RECORD
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
dnastack/__init__.py,sha256=mslf7se8vBSK_HkqWTGPdibeVhT4xyKXgzQBV7dEK1M,333
|
|
2
2
|
dnastack/__main__.py,sha256=EKmtIs4TBseQJi-OT_U6LqRyKLiyrGTBuTQg9zE-G2I,4376
|
|
3
|
-
dnastack/constants.py,sha256=
|
|
3
|
+
dnastack/constants.py,sha256=FWzsrWhHnjdT5Dhtj7quZvLSLEk9hGm_PX1HiHE3mA8,114
|
|
4
4
|
dnastack/feature_flags.py,sha256=RK_V_Ovncoe6NeTheAA_frP-kYkZC1fDlTbbup2KYG4,1419
|
|
5
5
|
dnastack/json_path.py,sha256=TyghhDf7nGQmnsUWBhenU_fKsE_Ez-HLVER6HgH5-hU,2700
|
|
6
6
|
dnastack/omics_cli.py,sha256=ZppKZTHv_XjUUZyRIzSkx0Ug5ODAYrCOTsU0ezCOVrA,3694
|
|
@@ -89,11 +89,13 @@ dnastack/cli/commands/workbench/storage/update.py,sha256=man43AfkFA-9STQiSH4_SuI
|
|
|
89
89
|
dnastack/cli/commands/workbench/storage/utils.py,sha256=uZPN6rx14y8Wymc2uDOiGj4imSJOWbDWMm1Fmta_pYE,3204
|
|
90
90
|
dnastack/cli/commands/workbench/workflows/__init__.py,sha256=GLUXWR2UD5ZOlvfcUv2o2oz_k7mmPWU8650egt3rDfM,513
|
|
91
91
|
dnastack/cli/commands/workbench/workflows/commands.py,sha256=xdMx45U7901F9fU3AZU7nOOeXCbqKio6CenCQpc-b6A,14036
|
|
92
|
-
dnastack/cli/commands/workbench/workflows/utils.py,sha256=
|
|
93
|
-
dnastack/cli/commands/workbench/workflows/versions/__init__.py,sha256=
|
|
92
|
+
dnastack/cli/commands/workbench/workflows/utils.py,sha256=a6Jp_odj8eICNZK0PxlP9zdaeEuq3IPjlKDrYCcLyvc,12160
|
|
93
|
+
dnastack/cli/commands/workbench/workflows/versions/__init__.py,sha256=tiVcCClMNuxmBwJEJQrvm8_t-ytzjOHaILAfAGFCoQY,979
|
|
94
94
|
dnastack/cli/commands/workbench/workflows/versions/commands.py,sha256=fS5YrQcTSbHUig8kDuD3daWatZtXnopUT4eyWuzQT5w,16150
|
|
95
95
|
dnastack/cli/commands/workbench/workflows/versions/defaults.py,sha256=NoDsUpkrFFLzw9J6l3ebdViwt6OaNFrmGxjv3yBFMak,12265
|
|
96
96
|
dnastack/cli/commands/workbench/workflows/versions/transformations.py,sha256=jrhi2UpOULM98F9yHjmomN7Ax2WIodOah5M95mY4Yfc,9271
|
|
97
|
+
dnastack/cli/commands/workbench/workflows/versions/dependencies/__init__.py,sha256=yiwey2vuwnUyZzN4J7b0bMNi81w1_gvyHf15Z4AjxCg,62
|
|
98
|
+
dnastack/cli/commands/workbench/workflows/versions/dependencies/commands.py,sha256=qUbV3wctaHznizDyst9gtHYshSfar7P5rAYPn6tBGsk,11810
|
|
97
99
|
dnastack/cli/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
100
|
dnastack/cli/core/command.py,sha256=H6tG2D7X6B0WvCdQdpcYE4QZSJ19A5nTYUq-NlHW-to,8031
|
|
99
101
|
dnastack/cli/core/command_formatting.py,sha256=be2SKCM0ZzOF8O5ySStGVBGD43rYAqbOP6Xx_dDYCTc,10428
|
|
@@ -138,7 +140,7 @@ dnastack/client/workbench/base_client.py,sha256=KlkSO1c32bKhojfco8NcVBVSY5x_PZAC
|
|
|
138
140
|
dnastack/client/workbench/models.py,sha256=RBo7wmWMSDkgiFZHaWh2ehKeTM8ERywug1bMGKDOm0k,446
|
|
139
141
|
dnastack/client/workbench/ewes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
140
142
|
dnastack/client/workbench/ewes/client.py,sha256=yIqjwyyY9q0NrxpTX6LrnlnjavHoa6Fo073O3Lokkaw,15637
|
|
141
|
-
dnastack/client/workbench/ewes/models.py,sha256=
|
|
143
|
+
dnastack/client/workbench/ewes/models.py,sha256=3XGVMW2eXlnd84BuluhjvNfKvxVrd06WTdZO6G4Kc7w,8394
|
|
142
144
|
dnastack/client/workbench/samples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
143
145
|
dnastack/client/workbench/samples/client.py,sha256=2X34SYTjV6n4yZz0q7Kaa4NPWDHRi2ut0uJWL3zXZWA,5901
|
|
144
146
|
dnastack/client/workbench/samples/models.py,sha256=g_04aDltLVRVCstOGkINqJNo1XSKB2aXWwnMfDEhC0Y,1466
|
|
@@ -149,8 +151,8 @@ dnastack/client/workbench/workbench_user_service/__init__.py,sha256=47DEQpj8HBSa
|
|
|
149
151
|
dnastack/client/workbench/workbench_user_service/client.py,sha256=ZpMOFw5L3NxbC2WtKpH3OJ435zEjy0-m4p0WgzQEOB0,1219
|
|
150
152
|
dnastack/client/workbench/workbench_user_service/models.py,sha256=P8WmocouYthi4gnHzNJT2F3iExWTt_2MUnskexN6Rxs,126
|
|
151
153
|
dnastack/client/workbench/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
152
|
-
dnastack/client/workbench/workflow/client.py,sha256=
|
|
153
|
-
dnastack/client/workbench/workflow/models.py,sha256=
|
|
154
|
+
dnastack/client/workbench/workflow/client.py,sha256=qdDBFTYQq4tluFXL22syS1RZoGHbX-ZQfVXSgWj5UmI,22244
|
|
155
|
+
dnastack/client/workbench/workflow/models.py,sha256=83_MLSLgFHa6OS6haX3QerUCtxEw93GCPE7D0Dq0vVo,5505
|
|
154
156
|
dnastack/client/workbench/workflow/utils.py,sha256=Yw9X-Gtu5lYPDCZjimFJMhrib9ELl07YyD4A-L8Y7pE,4661
|
|
155
157
|
dnastack/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
156
158
|
dnastack/common/auth_manager.py,sha256=AFMDIR01AQ2EPNyUZ7RMS8A8FvdRUMEhLtFjTd5Mdqw,9051
|
|
@@ -194,9 +196,9 @@ dnastack/http/authenticators/oauth2_adapter/device_code_flow.py,sha256=dXI5CyUcs
|
|
|
194
196
|
dnastack/http/authenticators/oauth2_adapter/factory.py,sha256=ZtNXOklWEim-26ooNoPp3ji_hRg1vf4fHHnY94F0wLI,1087
|
|
195
197
|
dnastack/http/authenticators/oauth2_adapter/models.py,sha256=iY7asrSElyjubInrGV5rJKKZAxJWeq7csnaj-EqMq00,943
|
|
196
198
|
dnastack/http/authenticators/oauth2_adapter/token_exchange.py,sha256=nSuAsSKWa_UNqHSbPMOEk4komaFITYAnE04Sk5WOrLc,6332
|
|
197
|
-
dnastack_client_library-3.1.
|
|
198
|
-
dnastack_client_library-3.1.
|
|
199
|
-
dnastack_client_library-3.1.
|
|
200
|
-
dnastack_client_library-3.1.
|
|
201
|
-
dnastack_client_library-3.1.
|
|
202
|
-
dnastack_client_library-3.1.
|
|
199
|
+
dnastack_client_library-3.1.170.dist-info/licenses/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
|
|
200
|
+
dnastack_client_library-3.1.170.dist-info/METADATA,sha256=HPiLitZ_avQH7XA2DOb3gKGkNP_eKU3pBVCVt3XXU9c,1766
|
|
201
|
+
dnastack_client_library-3.1.170.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
202
|
+
dnastack_client_library-3.1.170.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
|
|
203
|
+
dnastack_client_library-3.1.170.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
|
|
204
|
+
dnastack_client_library-3.1.170.dist-info/RECORD,,
|
{dnastack_client_library-3.1.168.dist-info → dnastack_client_library-3.1.170.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|