orchestrator-core 4.1.0rc1__py3-none-any.whl → 4.1.0rc2__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.
orchestrator/__init__.py CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  """This is the orchestrator workflow engine."""
15
15
 
16
- __version__ = "4.1.0rc1"
16
+ __version__ = "4.1.0rc2"
17
17
 
18
18
  from orchestrator.app import OrchestratorCore
19
19
  from orchestrator.settings import app_settings
@@ -25,7 +25,7 @@ from fastapi.param_functions import Body, Depends, Header
25
25
  from fastapi.routing import APIRouter
26
26
  from fastapi.websockets import WebSocket
27
27
  from fastapi_etag.dependency import CacheHit
28
- from more_itertools import chunked
28
+ from more_itertools import chunked, last
29
29
  from sentry_sdk.tracing import trace
30
30
  from sqlalchemy import CompoundSelect, Select, select
31
31
  from sqlalchemy.orm import defer, joinedload
@@ -56,6 +56,7 @@ from orchestrator.services.processes import (
56
56
  )
57
57
  from orchestrator.services.settings import get_engine_settings
58
58
  from orchestrator.settings import app_settings
59
+ from orchestrator.utils.auth import Authorizer
59
60
  from orchestrator.utils.enrich_process import enrich_process
60
61
  from orchestrator.websocket import (
61
62
  WS_CHANNELS,
@@ -63,7 +64,7 @@ from orchestrator.websocket import (
63
64
  broadcast_process_update_to_websocket,
64
65
  websocket_manager,
65
66
  )
66
- from orchestrator.workflow import ProcessStatus
67
+ from orchestrator.workflow import ProcessStat, ProcessStatus, StepList, Workflow
67
68
  from pydantic_forms.types import JSON, State
68
69
 
69
70
  router = APIRouter()
@@ -86,6 +87,48 @@ def check_global_lock() -> None:
86
87
  )
87
88
 
88
89
 
90
+ def get_current_steps(pstat: ProcessStat) -> StepList:
91
+ """Extract past and current steps from the ProcessStat."""
92
+ remaining_steps = pstat.log
93
+ past_steps = pstat.workflow.steps[: -len(remaining_steps)]
94
+ return StepList(past_steps + [pstat.log[0]])
95
+
96
+
97
+ def get_auth_callbacks(steps: StepList, workflow: Workflow) -> tuple[Authorizer | None, Authorizer | None]:
98
+ """Iterate over workflow and prior steps to determine correct authorization callbacks for the current step.
99
+
100
+ It's safest to always iterate through the steps. We could track these callbacks statefully
101
+ as we progress through the workflow, but if we fail a step and the system restarts, the previous
102
+ callbacks will be lost if they're only available in the process state.
103
+
104
+ Priority:
105
+ - RESUME callback is explicit RESUME callback, else previous START/RESUME callback
106
+ - RETRY callback is explicit RETRY, else explicit RESUME, else previous RETRY
107
+ """
108
+ # Default to workflow start callbacks
109
+ auth_resume = workflow.authorize_callback
110
+ # auth_retry defaults to the workflow start callback if not otherwise specified.
111
+ # A workflow SHOULD have both callbacks set to not-None. This enforces the correct default regardless.
112
+ auth_retry = workflow.retry_auth_callback or auth_resume # type: ignore[unreachable, truthy-function]
113
+
114
+ # Choose the most recently established value for resume.
115
+ auth_resume = last(filter(None, (step.resume_auth_callback for step in steps)), auth_resume)
116
+ # Choose the most recently established value for retry, unless there is a more recent value for resume.
117
+ auth_retry = last(
118
+ filter(None, (step.retry_auth_callback or step.resume_auth_callback for step in steps)), auth_retry
119
+ )
120
+ return auth_resume, auth_retry
121
+
122
+
123
+ def can_be_resumed(status: ProcessStatus) -> bool:
124
+ return status in (
125
+ ProcessStatus.SUSPENDED, # Can be resumed
126
+ ProcessStatus.FAILED, # Can be retried
127
+ ProcessStatus.API_UNAVAILABLE, # subtype of FAILED
128
+ ProcessStatus.INCONSISTENT_DATA, # subtype of FAILED
129
+ )
130
+
131
+
89
132
  def resolve_user_name(
90
133
  *,
91
134
  reporter: Reporter | None,
@@ -150,18 +193,25 @@ def new_process(
150
193
  dependencies=[Depends(check_global_lock, use_cache=False)],
151
194
  )
152
195
  def resume_process_endpoint(
153
- process_id: UUID, request: Request, json_data: JSON = Body(...), user: str = Depends(user_name)
196
+ process_id: UUID,
197
+ request: Request,
198
+ json_data: JSON = Body(...),
199
+ user: str = Depends(user_name),
200
+ user_model: OIDCUserModel | None = Depends(authenticate),
154
201
  ) -> None:
155
202
  process = _get_process(process_id)
156
203
 
157
- if process.last_status == ProcessStatus.COMPLETED:
158
- raise_status(HTTPStatus.CONFLICT, "Resuming a completed workflow is not possible")
159
-
160
- if process.last_status == ProcessStatus.RUNNING:
161
- raise_status(HTTPStatus.CONFLICT, "Resuming a running workflow is not possible")
162
-
163
- if process.last_status == ProcessStatus.RESUMED:
164
- raise_status(HTTPStatus.CONFLICT, "Resuming a resumed workflow is not possible")
204
+ if not can_be_resumed(process.last_status):
205
+ raise_status(HTTPStatus.CONFLICT, f"Resuming a {process.last_status.lower()} workflow is not possible")
206
+
207
+ pstat = load_process(process)
208
+ auth_resume, auth_retry = get_auth_callbacks(get_current_steps(pstat), pstat.workflow)
209
+ if process.last_status == ProcessStatus.SUSPENDED:
210
+ if auth_resume is not None and not auth_resume(user_model):
211
+ raise_status(HTTPStatus.FORBIDDEN, "User is not authorized to resume step")
212
+ elif process.last_status == ProcessStatus.FAILED:
213
+ if auth_retry is not None and not auth_retry(user_model):
214
+ raise_status(HTTPStatus.FORBIDDEN, "User is not authorized to retry step")
165
215
 
166
216
  broadcast_invalidate_status_counts()
167
217
  broadcast_func = api_broadcast_process_data(request)
@@ -19,6 +19,7 @@ import structlog
19
19
  from celery.result import AsyncResult
20
20
  from kombu.exceptions import ConnectionError, OperationalError
21
21
 
22
+ from oauth2_lib.fastapi import OIDCUserModel
22
23
  from orchestrator import app_settings
23
24
  from orchestrator.api.error_handling import raise_status
24
25
  from orchestrator.db import ProcessTable, db
@@ -42,7 +43,11 @@ def _block_when_testing(task_result: AsyncResult) -> None:
42
43
 
43
44
 
44
45
  def _celery_start_process(
45
- workflow_key: str, user_inputs: list[State] | None, user: str = SYSTEM_USER, **kwargs: Any
46
+ workflow_key: str,
47
+ user_inputs: list[State] | None,
48
+ user: str = SYSTEM_USER,
49
+ user_model: OIDCUserModel | None = None,
50
+ **kwargs: Any,
46
51
  ) -> UUID:
47
52
  """Client side call of Celery."""
48
53
  from orchestrator.services.tasks import NEW_TASK, NEW_WORKFLOW, get_celery_task
@@ -57,7 +62,7 @@ def _celery_start_process(
57
62
 
58
63
  task_name = NEW_TASK if wf_table.is_task else NEW_WORKFLOW
59
64
  trigger_task = get_celery_task(task_name)
60
- pstat = create_process(workflow_key, user_inputs, user)
65
+ pstat = create_process(workflow_key, user_inputs=user_inputs, user=user, user_model=user_model)
61
66
  try:
62
67
  result = trigger_task.delay(pstat.process_id, workflow_key, user)
63
68
  _block_when_testing(result)
@@ -467,9 +467,7 @@ def thread_start_process(
467
467
  user_model: OIDCUserModel | None = None,
468
468
  broadcast_func: BroadcastFunc | None = None,
469
469
  ) -> UUID:
470
- pstat = create_process(workflow_key, user_inputs=user_inputs, user=user)
471
- if not pstat.workflow.authorize_callback(user_model):
472
- raise_status(HTTPStatus.FORBIDDEN, error_message_unauthorized(workflow_key))
470
+ pstat = create_process(workflow_key, user_inputs=user_inputs, user=user, user_model=user_model)
473
471
 
474
472
  _safe_logstep_with_func = partial(safe_logstep, broadcast_func=broadcast_func)
475
473
  return _run_process_async(pstat.process_id, lambda: runwf(pstat, _safe_logstep_with_func))
@@ -506,7 +504,6 @@ def thread_resume_process(
506
504
  *,
507
505
  user_inputs: list[State] | None = None,
508
506
  user: str | None = None,
509
- user_model: OIDCUserModel | None = None,
510
507
  broadcast_func: BroadcastFunc | None = None,
511
508
  ) -> UUID:
512
509
  # ATTENTION!! When modifying this function make sure you make similar changes to `resume_workflow` in the test code
@@ -515,8 +512,6 @@ def thread_resume_process(
515
512
  user_inputs = [{}]
516
513
 
517
514
  pstat = load_process(process)
518
- if not pstat.workflow.authorize_callback(user_model):
519
- raise_status(HTTPStatus.FORBIDDEN, error_message_unauthorized(str(process.workflow_name)))
520
515
 
521
516
  if pstat.workflow == removed_workflow:
522
517
  raise ValueError("This workflow cannot be resumed")
@@ -556,7 +551,6 @@ def resume_process(
556
551
  *,
557
552
  user_inputs: list[State] | None = None,
558
553
  user: str | None = None,
559
- user_model: OIDCUserModel | None = None,
560
554
  broadcast_func: BroadcastFunc | None = None,
561
555
  ) -> UUID:
562
556
  """Resume a failed or suspended process.
@@ -565,7 +559,6 @@ def resume_process(
565
559
  process: Process from database
566
560
  user_inputs: Optional user input from forms
567
561
  user: user who resumed this process
568
- user_model: OIDCUserModel of user who resumed this process
569
562
  broadcast_func: Optional function to broadcast process data
570
563
 
571
564
  Returns:
@@ -573,8 +566,6 @@ def resume_process(
573
566
 
574
567
  """
575
568
  pstat = load_process(process)
576
- if not pstat.workflow.authorize_callback(user_model):
577
- raise_status(HTTPStatus.FORBIDDEN, error_message_unauthorized(str(process.workflow_name)))
578
569
 
579
570
  try:
580
571
  post_form(pstat.log[0].form, pstat.state.unwrap(), user_inputs=user_inputs or [])
@@ -0,0 +1,9 @@
1
+ from collections.abc import Callable
2
+ from typing import TypeAlias
3
+
4
+ from oauth2_lib.fastapi import OIDCUserModel
5
+
6
+ # This file is broken out separately to avoid circular imports.
7
+
8
+ # Can instead use "type Authorizer = ..." in later Python versions.
9
+ Authorizer: TypeAlias = Callable[[OIDCUserModel | None], bool]
orchestrator/workflow.py CHANGED
@@ -45,6 +45,7 @@ from orchestrator.db import db, transactional
45
45
  from orchestrator.services.settings import get_engine_settings
46
46
  from orchestrator.targets import Target
47
47
  from orchestrator.types import ErrorDict, StepFunc
48
+ from orchestrator.utils.auth import Authorizer
48
49
  from orchestrator.utils.docs import make_workflow_doc
49
50
  from orchestrator.utils.errors import error_state_to_dict
50
51
  from orchestrator.utils.state import form_inject_args, inject_args
@@ -80,6 +81,8 @@ class Step(Protocol):
80
81
  name: str
81
82
  form: InputFormGenerator | None
82
83
  assignee: Assignee | None
84
+ resume_auth_callback: Authorizer | None = None
85
+ retry_auth_callback: Authorizer | None = None
83
86
 
84
87
  def __call__(self, state: State) -> Process: ...
85
88
 
@@ -90,7 +93,8 @@ class Workflow(Protocol):
90
93
  __qualname__: str
91
94
  name: str
92
95
  description: str
93
- authorize_callback: Callable[[OIDCUserModel | None], bool]
96
+ authorize_callback: Authorizer
97
+ retry_auth_callback: Authorizer
94
98
  initial_input_form: InputFormGenerator | None = None
95
99
  target: Target
96
100
  steps: StepList
@@ -99,13 +103,20 @@ class Workflow(Protocol):
99
103
 
100
104
 
101
105
  def make_step_function(
102
- f: Callable, name: str, form: InputFormGenerator | None = None, assignee: Assignee | None = Assignee.SYSTEM
106
+ f: Callable,
107
+ name: str,
108
+ form: InputFormGenerator | None = None,
109
+ assignee: Assignee | None = Assignee.SYSTEM,
110
+ resume_auth_callback: Authorizer | None = None,
111
+ retry_auth_callback: Authorizer | None = None,
103
112
  ) -> Step:
104
113
  step_func = cast(Step, f)
105
114
 
106
115
  step_func.name = name
107
116
  step_func.form = form
108
117
  step_func.assignee = assignee
118
+ step_func.resume_auth_callback = resume_auth_callback
119
+ step_func.retry_auth_callback = retry_auth_callback
109
120
  return step_func
110
121
 
111
122
 
@@ -167,6 +178,7 @@ class StepList(list[Step]):
167
178
 
168
179
 
169
180
  def _handle_simple_input_form_generator(f: StateInputStepFunc) -> StateInputFormGenerator:
181
+ """Processes f into a form generator and injects a pre-hook for user authorization."""
170
182
  if inspect.isgeneratorfunction(f):
171
183
  return cast(StateInputFormGenerator, f)
172
184
  if inspect.isgenerator(f):
@@ -191,7 +203,8 @@ def make_workflow(
191
203
  initial_input_form: InputStepFunc | None,
192
204
  target: Target,
193
205
  steps: StepList,
194
- authorize_callback: Callable[[OIDCUserModel | None], bool] | None = None,
206
+ authorize_callback: Authorizer | None = None,
207
+ retry_auth_callback: Authorizer | None = None,
195
208
  ) -> Workflow:
196
209
  @functools.wraps(f)
197
210
  def wrapping_function() -> NoReturn:
@@ -202,6 +215,10 @@ def make_workflow(
202
215
  wrapping_function.name = f.__name__ # default, will be changed by LazyWorkflowInstance
203
216
  wrapping_function.description = description
204
217
  wrapping_function.authorize_callback = allow if authorize_callback is None else authorize_callback
218
+ # If no retry auth policy is given, defer to policy for process creation.
219
+ wrapping_function.retry_auth_callback = (
220
+ wrapping_function.authorize_callback if retry_auth_callback is None else retry_auth_callback
221
+ )
205
222
 
206
223
  if initial_input_form is None:
207
224
  # We always need a form to prevent starting a workflow when no input is needed.
@@ -270,9 +287,16 @@ def retrystep(name: str) -> Callable[[StepFunc], Step]:
270
287
  return decorator
271
288
 
272
289
 
273
- def inputstep(name: str, assignee: Assignee) -> Callable[[InputStepFunc], Step]:
290
+ def inputstep(
291
+ name: str,
292
+ assignee: Assignee,
293
+ resume_auth_callback: Authorizer | None = None,
294
+ retry_auth_callback: Authorizer | None = None,
295
+ ) -> Callable[[InputStepFunc], Step]:
274
296
  """Add user input step to workflow.
275
297
 
298
+ Any authorization callbacks will be attached to the resulting Step.
299
+
276
300
  IMPORTANT: In contrast to other workflow steps, the `@inputstep` wrapped function will not run in the
277
301
  workflow engine! This means that it must be free of side effects!
278
302
 
@@ -299,7 +323,14 @@ def inputstep(name: str, assignee: Assignee) -> Callable[[InputStepFunc], Step]:
299
323
  def suspend(state: State) -> Process:
300
324
  return Suspend(state)
301
325
 
302
- return make_step_function(suspend, name, wrapper, assignee)
326
+ return make_step_function(
327
+ suspend,
328
+ name,
329
+ wrapper,
330
+ assignee,
331
+ resume_auth_callback=resume_auth_callback,
332
+ retry_auth_callback=retry_auth_callback,
333
+ )
303
334
 
304
335
  return decorator
305
336
 
@@ -479,7 +510,8 @@ def workflow(
479
510
  description: str,
480
511
  initial_input_form: InputStepFunc | None = None,
481
512
  target: Target = Target.SYSTEM,
482
- authorize_callback: Callable[[OIDCUserModel | None], bool] | None = None,
513
+ authorize_callback: Authorizer | None = None,
514
+ retry_auth_callback: Authorizer | None = None,
483
515
  ) -> Callable[[Callable[[], StepList]], Workflow]:
484
516
  """Transform an initial_input_form and a step list into a workflow.
485
517
 
@@ -500,7 +532,13 @@ def workflow(
500
532
 
501
533
  def _workflow(f: Callable[[], StepList]) -> Workflow:
502
534
  return make_workflow(
503
- f, description, initial_input_form_in_form_inject_args, target, f(), authorize_callback=authorize_callback
535
+ f,
536
+ description,
537
+ initial_input_form_in_form_inject_args,
538
+ target,
539
+ f(),
540
+ authorize_callback=authorize_callback,
541
+ retry_auth_callback=retry_auth_callback,
504
542
  )
505
543
 
506
544
  return _workflow
@@ -20,12 +20,12 @@ from more_itertools import first_true
20
20
  from pydantic import field_validator, model_validator
21
21
  from sqlalchemy import select
22
22
 
23
- from oauth2_lib.fastapi import OIDCUserModel
24
23
  from orchestrator.db import ProductTable, SubscriptionTable, db
25
24
  from orchestrator.forms.validators import ProductId
26
25
  from orchestrator.services import subscriptions
27
26
  from orchestrator.targets import Target
28
27
  from orchestrator.types import SubscriptionLifecycle
28
+ from orchestrator.utils.auth import Authorizer
29
29
  from orchestrator.utils.errors import StaleDataError
30
30
  from orchestrator.utils.state import form_inject_args
31
31
  from orchestrator.utils.validate_data_version import validate_data_version
@@ -201,7 +201,8 @@ def create_workflow(
201
201
  initial_input_form: InputStepFunc | None = None,
202
202
  status: SubscriptionLifecycle = SubscriptionLifecycle.ACTIVE,
203
203
  additional_steps: StepList | None = None,
204
- authorize_callback: Callable[[OIDCUserModel | None], bool] | None = None,
204
+ authorize_callback: Authorizer | None = None,
205
+ retry_auth_callback: Authorizer | None = None,
205
206
  ) -> Callable[[Callable[[], StepList]], Workflow]:
206
207
  """Transform an initial_input_form and a step list into a workflow with a target=Target.CREATE.
207
208
 
@@ -234,6 +235,7 @@ def create_workflow(
234
235
  Target.CREATE,
235
236
  steplist,
236
237
  authorize_callback=authorize_callback,
238
+ retry_auth_callback=retry_auth_callback,
237
239
  )
238
240
 
239
241
  return _create_workflow
@@ -243,7 +245,8 @@ def modify_workflow(
243
245
  description: str,
244
246
  initial_input_form: InputStepFunc | None = None,
245
247
  additional_steps: StepList | None = None,
246
- authorize_callback: Callable[[OIDCUserModel | None], bool] | None = None,
248
+ authorize_callback: Authorizer | None = None,
249
+ retry_auth_callback: Authorizer | None = None,
247
250
  ) -> Callable[[Callable[[], StepList]], Workflow]:
248
251
  """Transform an initial_input_form and a step list into a workflow.
249
252
 
@@ -278,6 +281,7 @@ def modify_workflow(
278
281
  Target.MODIFY,
279
282
  steplist,
280
283
  authorize_callback=authorize_callback,
284
+ retry_auth_callback=retry_auth_callback,
281
285
  )
282
286
 
283
287
  return _modify_workflow
@@ -287,7 +291,8 @@ def terminate_workflow(
287
291
  description: str,
288
292
  initial_input_form: InputStepFunc | None = None,
289
293
  additional_steps: StepList | None = None,
290
- authorize_callback: Callable[[OIDCUserModel | None], bool] | None = None,
294
+ authorize_callback: Authorizer | None = None,
295
+ retry_auth_callback: Authorizer | None = None,
291
296
  ) -> Callable[[Callable[[], StepList]], Workflow]:
292
297
  """Transform an initial_input_form and a step list into a workflow.
293
298
 
@@ -323,6 +328,7 @@ def terminate_workflow(
323
328
  Target.TERMINATE,
324
329
  steplist,
325
330
  authorize_callback=authorize_callback,
331
+ retry_auth_callback=retry_auth_callback,
326
332
  )
327
333
 
328
334
  return _terminate_workflow
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orchestrator-core
3
- Version: 4.1.0rc1
3
+ Version: 4.1.0rc2
4
4
  Summary: This is the orchestrator workflow engine.
5
5
  Requires-Python: >=3.11,<3.14
6
6
  Classifier: Intended Audience :: Information Technology
@@ -1,4 +1,4 @@
1
- orchestrator/__init__.py,sha256=tOY8a4U2sGqlMaBFFS05G5fpTsG5MQWrMraMt7i-SW0,1066
1
+ orchestrator/__init__.py,sha256=rvhBWDPCO-2bbuNrSMvfh1UqquJITXBYE2zo4tsKmWo,1066
2
2
  orchestrator/app.py,sha256=7UrXKjBKNSEaSSXAd5ww_RdMFhFqE4yvfj8faS2MzAA,12089
3
3
  orchestrator/exception_handlers.py,sha256=UsW3dw8q0QQlNLcV359bIotah8DYjMsj2Ts1LfX4ClY,1268
4
4
  orchestrator/log_config.py,sha256=1tPRX5q65e57a6a_zEii_PFK8SzWT0mnA5w2sKg4hh8,1853
@@ -8,7 +8,7 @@ orchestrator/settings.py,sha256=TFIv09JIKY-lXqd04lH_XEcijEEyheaz3zTcgeG8DEI,4339
8
8
  orchestrator/targets.py,sha256=WizBgnp8hWX9YLFUIju7ewSubiwQqinCvyiYNcXHbHI,802
9
9
  orchestrator/types.py,sha256=qzs7xx5AYRmKbpYRyJJP3wuDb0W0bcAzefCN0RWLAco,15459
10
10
  orchestrator/version.py,sha256=b58e08lxs47wUNXv0jXFO_ykpksmytuzEXD4La4W-NQ,1366
11
- orchestrator/workflow.py,sha256=T_3Z1PrlF70C16ehAthKRF1hd3jpwV-MREDVxSLfxOw,44063
11
+ orchestrator/workflow.py,sha256=N4KGmH_Nf0U6nTxs62-qby7WVIoppHF6uKrZKCdxwfQ,45285
12
12
  orchestrator/api/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
13
13
  orchestrator/api/error_handling.py,sha256=YrPCxSa-DSa9KwqIMlXI-KGBGnbGIW5ukOPiikUH9E4,1502
14
14
  orchestrator/api/helpers.py,sha256=s0QRHYw8AvEmlkmRhuEzz9xixaZKUF3YuPzUVHkcoXk,6933
@@ -17,7 +17,7 @@ orchestrator/api/api_v1/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n
17
17
  orchestrator/api/api_v1/api.py,sha256=m4iDktsSpzxUDaudkdgXeZ83a6B4wfc3pczQsa-Pb-8,2866
18
18
  orchestrator/api/api_v1/endpoints/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
19
19
  orchestrator/api/api_v1/endpoints/health.py,sha256=iaxs1XX1_250_gKNsspuULCV2GEMBjbtjsmfQTOvMAI,1284
20
- orchestrator/api/api_v1/endpoints/processes.py,sha256=i5GRoszvdmpZqOxSqON83DNuc0UIV-ohOqbAa0OhESE,13564
20
+ orchestrator/api/api_v1/endpoints/processes.py,sha256=Ah_Dc9ONXDpaFzLTqSNdhkGPHC7vUBkWmx1fWsEwbzo,16076
21
21
  orchestrator/api/api_v1/endpoints/product_blocks.py,sha256=kZ6ywIOsS_S2qGq7RvZ4KzjvaS1LmwbGWR37AKRvWOw,2146
22
22
  orchestrator/api/api_v1/endpoints/products.py,sha256=BfFtwu9dZXEQbtKxYj9icc73GKGvAGMR5ytyf41nQlQ,3081
23
23
  orchestrator/api/api_v1/endpoints/resource_types.py,sha256=gGyuaDyOD0TAVoeFGaGmjDGnQ8eQQArOxKrrk4MaDzA,2145
@@ -260,11 +260,11 @@ orchestrator/schemas/subscription.py,sha256=-jXyHZIed9Xlia18ksSDyenblNN6Q2yM2FlG
260
260
  orchestrator/schemas/subscription_descriptions.py,sha256=Ft_jw1U0bf9Z0U8O4OWfLlcl0mXCVT_qYVagBP3GbIQ,1262
261
261
  orchestrator/schemas/workflow.py,sha256=VqQ9XfV4fVd6MjY0LRRQzWBJHmlPsAamWfTwDx1cZkg,2102
262
262
  orchestrator/services/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
263
- orchestrator/services/celery.py,sha256=SmAUsN755yE7cZ3og92qTvPPeRIpdEKlbaLih7o38h8,5089
263
+ orchestrator/services/celery.py,sha256=PsIgRBJsmA3vKwAUaqPq9ynLwDsXHY2ggDWc-nQAwgM,5232
264
264
  orchestrator/services/fixed_inputs.py,sha256=kyz7s2HLzyDulvcq-ZqefTw1om86COvyvTjz0_5CmgI,876
265
265
  orchestrator/services/input_state.py,sha256=HF7wl9fWdaAW8pdCCqbuYoKyNj8dY0g8Ff8vXis8z5A,2211
266
266
  orchestrator/services/process_broadcast_thread.py,sha256=D44YbjF8mRqGuznkRUV4SoRn1J0lfy_x1H508GnSVlU,4649
267
- orchestrator/services/processes.py,sha256=rTH6zLNsun3qDCPguz2LYS87MQR_LJREIPrgkGS6kwk,30494
267
+ orchestrator/services/processes.py,sha256=gXWdsJK8m_KwM4g-8LBQMb1tEcjUJWWIKmqobTE7OmU,29903
268
268
  orchestrator/services/products.py,sha256=BP4KyE8zO-8z7Trrs5T6zKBOw53S9BfBJnHWI3p6u5Y,1943
269
269
  orchestrator/services/resource_types.py,sha256=_QBy_JOW_X3aSTqH0CuLrq4zBJL0p7Q-UDJUcuK2_qc,884
270
270
  orchestrator/services/settings.py,sha256=HEWfFulgoEDwgfxGEO__QTr5fDiwNBEj1UhAeTAdbLQ,3159
@@ -275,6 +275,7 @@ orchestrator/services/tasks.py,sha256=NjPkuauQoh9UJDcjA7OcKFgPk0i6NoKdDO7HlpGbBJ
275
275
  orchestrator/services/translations.py,sha256=GyP8soUFGej8AS8uulBsk10CCK6Kwfjv9AHMFm3ElQY,1713
276
276
  orchestrator/services/workflows.py,sha256=iEkt2OBuTwkDru4V6ZSKatnw0b96ZdPV-VQqeZ9EOgU,4015
277
277
  orchestrator/utils/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
278
+ orchestrator/utils/auth.py,sha256=IWn0amdquLobt1mRNwhgKT0ErCBjLGDtLdsDuaY8rlE,309
278
279
  orchestrator/utils/crypt.py,sha256=18eNamYWMllPkxyRtWIde3FDr3rSF74R5SAL6WsCj9Y,5584
279
280
  orchestrator/utils/datetime.py,sha256=a1WQ_yvu7MA0TiaRpC5avwbOSFdrj4eMrV4a7I2sD5Q,1477
280
281
  orchestrator/utils/deprecation_logger.py,sha256=oqju7ecJcB_r7cMnldaOAA79QUZYS_h69IkDrFV9nAg,875
@@ -302,14 +303,14 @@ orchestrator/workflows/__init__.py,sha256=NzIGGI-8SNAwCk2YqH6sHhEWbgAY457ntDwjO1
302
303
  orchestrator/workflows/modify_note.py,sha256=l6QtijRPv8gnHxzwTz_4nWIPcZ0FcKQh_yFbtjYEDMg,2163
303
304
  orchestrator/workflows/removed_workflow.py,sha256=V0Da5TEdfLdZZKD38ig-MTp3_IuE7VGqzHHzvPYQmLI,909
304
305
  orchestrator/workflows/steps.py,sha256=ulpheoHaCOE1qh71Bja4KW4pItQh1Z6q4QU4tn5GtNk,6067
305
- orchestrator/workflows/utils.py,sha256=yBwfITlseimyLEzbwI0ehOAlr-xI1N3WGVudFz4boXk,13778
306
+ orchestrator/workflows/utils.py,sha256=9j0hQOvGnjXUuVUMMzSIpdiyT5NzijhLh1sBXbd5QJA,14008
306
307
  orchestrator/workflows/tasks/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
307
308
  orchestrator/workflows/tasks/cleanup_tasks_log.py,sha256=BfWYbPXhnLAHUJ0mlODDnjZnQQAvKCZJDVTwbwOWI04,1624
308
309
  orchestrator/workflows/tasks/resume_workflows.py,sha256=MzJqlSXUvKStkT7NGzxZyRlfAer_ezYm-kjUqaZi0yc,2359
309
310
  orchestrator/workflows/tasks/validate_product_type.py,sha256=paG-NAY1bdde3Adt8zItkcBKf5Pxw6f5ngGW6an6dYU,3192
310
311
  orchestrator/workflows/tasks/validate_products.py,sha256=GZJBoFF-WMphS7ghMs2-gqvV2iL1F0POhk0uSNt93n0,8510
311
312
  orchestrator/workflows/translations/en-GB.json,sha256=ST53HxkphFLTMjFHonykDBOZ7-P_KxksktZU3GbxLt0,846
312
- orchestrator_core-4.1.0rc1.dist-info/licenses/LICENSE,sha256=b-aA5OZQuuBATmLKo_mln8CQrDPPhg3ghLzjPjLn4Tg,11409
313
- orchestrator_core-4.1.0rc1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
314
- orchestrator_core-4.1.0rc1.dist-info/METADATA,sha256=hyIGgttPRSo2gjx2V1kbbpd5MvEbnCttnUJ2LzZEyP4,5073
315
- orchestrator_core-4.1.0rc1.dist-info/RECORD,,
313
+ orchestrator_core-4.1.0rc2.dist-info/licenses/LICENSE,sha256=b-aA5OZQuuBATmLKo_mln8CQrDPPhg3ghLzjPjLn4Tg,11409
314
+ orchestrator_core-4.1.0rc2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
315
+ orchestrator_core-4.1.0rc2.dist-info/METADATA,sha256=Ls33kV9YCHIfgDAD1pZ-7e4_EcsKBpWZ8iRSZCDQk-Y,5073
316
+ orchestrator_core-4.1.0rc2.dist-info/RECORD,,