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.
@@ -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,3 @@
1
+ from .commands import dependencies
2
+
3
+ __all__ = ['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[Dict[str, RunDependency]]
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[Dict[str, RunDependency]]
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
@@ -1,5 +1,5 @@
1
1
  import os
2
2
 
3
- __version__ = "v3.1.168"
3
+ __version__ = "v3.1.170"
4
4
 
5
5
  LOCAL_STORAGE_DIRECTORY = os.path.join(os.path.expanduser("~"), '.dnastack')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dnastack-client-library
3
- Version: 3.1.168
3
+ Version: 3.1.170
4
4
  Summary: DNAstack's GA4GH library and CLI
5
5
  Author-email: DNAstack <devs@dnastack.com>
6
6
  License: Apache License, Version 2.0
@@ -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=uuuxY_BJ60LgFuqhtfDC10aGSLwSJkTa4X8j8IyE1Zg,114
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=EtMMmGtl8xXNxu7kisimqhLd2bh2CvTV_lO3KSBuPl0,8636
93
- dnastack/cli/commands/workbench/workflows/versions/__init__.py,sha256=Bqowv86D8aHPb8w-aTF7hfcGAkW4emScnEZEs5Ve8CA,831
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=MBSkYxFW8nX9OlzijJxzZ3I7zCUFHM83PXF4zXbnZeY,8404
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=ZbQAzJ1DPoPcMGkJEKSyV37pEpSkphT_2WwFbHHkMbw,17872
153
- dnastack/client/workbench/workflow/models.py,sha256=xokiS_Kyet3zbyB8i5Z3Uq3wmvcgPMkTQ2WGWhFrexw,4481
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.168.dist-info/licenses/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
198
- dnastack_client_library-3.1.168.dist-info/METADATA,sha256=q6nHnGlEfbHDHArIU0ZQ7VofZcaq_7rVDM9ZBW8q-JQ,1766
199
- dnastack_client_library-3.1.168.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
200
- dnastack_client_library-3.1.168.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
201
- dnastack_client_library-3.1.168.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
202
- dnastack_client_library-3.1.168.dist-info/RECORD,,
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,,