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 +1 -1
- orchestrator/api/api_v1/endpoints/processes.py +61 -11
- orchestrator/services/celery.py +7 -2
- orchestrator/services/processes.py +1 -10
- orchestrator/utils/auth.py +9 -0
- orchestrator/workflow.py +45 -7
- orchestrator/workflows/utils.py +10 -4
- {orchestrator_core-4.1.0rc1.dist-info → orchestrator_core-4.1.0rc2.dist-info}/METADATA +1 -1
- {orchestrator_core-4.1.0rc1.dist-info → orchestrator_core-4.1.0rc2.dist-info}/RECORD +11 -10
- {orchestrator_core-4.1.0rc1.dist-info → orchestrator_core-4.1.0rc2.dist-info}/WHEEL +0 -0
- {orchestrator_core-4.1.0rc1.dist-info → orchestrator_core-4.1.0rc2.dist-info}/licenses/LICENSE +0 -0
orchestrator/__init__.py
CHANGED
|
@@ -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,
|
|
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
|
|
158
|
-
raise_status(HTTPStatus.CONFLICT, "Resuming a
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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)
|
orchestrator/services/celery.py
CHANGED
|
@@ -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,
|
|
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:
|
|
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,
|
|
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:
|
|
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(
|
|
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(
|
|
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:
|
|
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,
|
|
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
|
orchestrator/workflows/utils.py
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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,4 +1,4 @@
|
|
|
1
|
-
orchestrator/__init__.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
313
|
-
orchestrator_core-4.1.
|
|
314
|
-
orchestrator_core-4.1.
|
|
315
|
-
orchestrator_core-4.1.
|
|
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,,
|
|
File without changes
|
{orchestrator_core-4.1.0rc1.dist-info → orchestrator_core-4.1.0rc2.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|