orchestrator-core 4.3.0rc2__py3-none-any.whl → 4.3.1__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.3.0rc2"
16
+ __version__ = "4.3.1"
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, last
28
+ from more_itertools import chunked, first, 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
@@ -88,11 +88,17 @@ def check_global_lock() -> None:
88
88
  )
89
89
 
90
90
 
91
- def get_current_steps(pstat: ProcessStat) -> StepList:
92
- """Extract past and current steps from the ProcessStat."""
93
- remaining_steps = pstat.log
91
+ def get_steps_to_evaluate_for_rbac(pstat: ProcessStat) -> StepList:
92
+ """Extract all steps from the ProcessStat for a process that should be evaluated for a RBAC callback.
93
+
94
+ For a suspended process this includes all previously completed steps as well as the current step.
95
+ For a completed process this includes all steps.
96
+ """
97
+ if not (remaining_steps := pstat.log):
98
+ return pstat.workflow.steps
99
+
94
100
  past_steps = pstat.workflow.steps[: -len(remaining_steps)]
95
- return StepList(past_steps + [pstat.log[0]])
101
+ return StepList(past_steps >> first(remaining_steps))
96
102
 
97
103
 
98
104
  def get_auth_callbacks(steps: StepList, workflow: Workflow) -> tuple[Authorizer | None, Authorizer | None]:
@@ -200,7 +206,7 @@ def resume_process_endpoint(
200
206
  raise_status(HTTPStatus.CONFLICT, f"Resuming a {process.last_status.lower()} workflow is not possible")
201
207
 
202
208
  pstat = load_process(process)
203
- auth_resume, auth_retry = get_auth_callbacks(get_current_steps(pstat), pstat.workflow)
209
+ auth_resume, auth_retry = get_auth_callbacks(get_steps_to_evaluate_for_rbac(pstat), pstat.workflow)
204
210
  if process.last_status == ProcessStatus.SUSPENDED:
205
211
  if auth_resume is not None and not auth_resume(user_model):
206
212
  raise_status(HTTPStatus.FORBIDDEN, "User is not authorized to resume step")
@@ -6,7 +6,7 @@ from strawberry.federation.schema_directives import Key
6
6
  from strawberry.scalars import JSON
7
7
 
8
8
  from oauth2_lib.strawberry import authenticated_field
9
- from orchestrator.api.api_v1.endpoints.processes import get_auth_callbacks, get_current_steps
9
+ from orchestrator.api.api_v1.endpoints.processes import get_auth_callbacks, get_steps_to_evaluate_for_rbac
10
10
  from orchestrator.db import ProcessTable, ProductTable, db
11
11
  from orchestrator.graphql.pagination import EMPTY_PAGE, Connection
12
12
  from orchestrator.graphql.schemas.customer import CustomerType
@@ -86,7 +86,7 @@ class ProcessType:
86
86
  oidc_user = info.context.get_current_user
87
87
  workflow = get_workflow(self.workflow_name)
88
88
  process = load_process(db.session.get(ProcessTable, self.process_id)) # type: ignore[arg-type]
89
- auth_resume, auth_retry = get_auth_callbacks(get_current_steps(process), workflow) # type: ignore[arg-type]
89
+ auth_resume, auth_retry = get_auth_callbacks(get_steps_to_evaluate_for_rbac(process), workflow) # type: ignore[arg-type]
90
90
 
91
91
  return FormUserPermissionsType(
92
92
  retryAllowed=auth_retry and auth_retry(oidc_user), # type: ignore[arg-type]
File without changes
@@ -41,7 +41,7 @@ def retrieve_input_state(process_id: UUID, input_type: InputType, raise_exceptio
41
41
  select(InputStateTable)
42
42
  .filter(InputStateTable.process_id == process_id)
43
43
  .filter(InputStateTable.input_type == input_type)
44
- .order_by(InputStateTable.input_time.asc())
44
+ .order_by(InputStateTable.input_time.desc())
45
45
  ).first()
46
46
 
47
47
  if res:
@@ -733,11 +733,17 @@ def abort_process(process: ProcessTable, user: str, broadcast_func: Callable | N
733
733
 
734
734
 
735
735
  def _recoverwf(wf: Workflow, log: list[WFProcess]) -> tuple[WFProcess, StepList]:
736
- # Remove all extra steps (Failed, Suspended and (A)waiting steps in db). Only keep cleared steps.
736
+ """Recover workflow state and remaining steps from the given 'Process step' objects.
737
737
 
738
- persistent = list(
739
- filter(lambda p: not (p.isfailed() or p.issuspend() or p.iswaiting() or p.isawaitingcallback()), log)
740
- )
738
+ Returns:
739
+ - The state accumulated up until the last cleared (completed) step\
740
+ - The remaining steps to execute (including Failed, Suspended and (A)waiting steps)
741
+ """
742
+
743
+ def is_cleared(p: WFProcess) -> bool:
744
+ return not (p.isfailed() or p.issuspend() or p.iswaiting() or p.isawaitingcallback())
745
+
746
+ persistent = [p for p in log if is_cleared(p)]
741
747
  stepcount = len(persistent)
742
748
 
743
749
  if log and (log[-1].issuspend() or log[-1].isawaitingcallback()):
@@ -760,15 +766,14 @@ def _recoverwf(wf: Workflow, log: list[WFProcess]) -> tuple[WFProcess, StepList]
760
766
 
761
767
 
762
768
  def _restore_log(steps: list[ProcessStepTable]) -> list[WFProcess]:
763
- result = []
764
- for step in steps:
765
- process = WFProcess.from_status(step.status, step.state)
769
+ """Deserialize ProcessStepTable objects into foldable 'Process step' objects."""
766
770
 
767
- if not process:
768
- raise ValueError(step.status)
771
+ def deserialize(step: ProcessStepTable) -> WFProcess:
772
+ if not (wf_process := WFProcess.from_status(step.status, step.state)):
773
+ raise ValueError(f"Unable to deserialize step from it's status {step.status}")
774
+ return wf_process
769
775
 
770
- result.append(process)
771
- return result
776
+ return [deserialize(step) for step in steps]
772
777
 
773
778
 
774
779
  def load_process(process: ProcessTable) -> ProcessStat:
orchestrator/workflow.py CHANGED
@@ -552,7 +552,7 @@ class ProcessStat:
552
552
  process_id: UUID
553
553
  workflow: Workflow
554
554
  state: Process
555
- log: StepList
555
+ log: StepList # Remaining steps to execute
556
556
  current_user: str
557
557
  user_model: OIDCUserModel | None = None
558
558
 
@@ -597,6 +597,13 @@ class StepStatus(strEnum):
597
597
 
598
598
 
599
599
  class Process(Generic[S]):
600
+ """ADT base class.
601
+
602
+ This class defines an Algebraic Data Type - specifically a "sum type" - that defines the possible
603
+ variants of a Process. It encapsulates the state and allows to fold _instances_ of a process into
604
+ a single value. These instances correspond to subsequent steps of the process.
605
+ """
606
+
600
607
  def __init__(self, s: S):
601
608
  self.s = s
602
609
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orchestrator-core
3
- Version: 4.3.0rc2
3
+ Version: 4.3.1
4
4
  Summary: This is the orchestrator workflow engine.
5
5
  Author-email: SURF <automation-beheer@surf.nl>
6
6
  Requires-Python: >=3.11,<3.14
@@ -1,4 +1,4 @@
1
- orchestrator/__init__.py,sha256=k7E0AiB5QFgGg_GlFGkYHF-DTfhWCYkziVwvIXSWeAc,1066
1
+ orchestrator/__init__.py,sha256=A485Z6oxvKfpqVhk24XIbDwJZrxQE0dCBgzWyjrzkOw,1063
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=2Kgc6m3qUCcSM3Z_IVUeehfgO0QphMFkLrS0RC3sU-U,4365
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=PVHe6vnnkswzqw2UoY-j6NMSEhL6rLHXRnO7yLOyDC8,45551
11
+ orchestrator/workflow.py,sha256=meDCPnyyX_n5PsMUaFy2wWb5EKNm1_ff7zRDBYrbcDg,45901
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=kWz_jL8_sTNwl44tU17VwkwZGjBIw1IIW5pYCCSHwgs,15891
20
+ orchestrator/api/api_v1/endpoints/processes.py,sha256=238Bydgj4ILNyMU_7j_Q7a0WGlfIvKv5ypP7lESU32w,16188
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
@@ -184,7 +184,7 @@ orchestrator/graphql/schemas/customer_description.py,sha256=fize71IMpkvk_rTzcqCY
184
184
  orchestrator/graphql/schemas/errors.py,sha256=VRl-Zd1FHMnscyozhfxzqeEUZ0ERAWum_Y8YwjGxwmA,203
185
185
  orchestrator/graphql/schemas/fixed_input.py,sha256=1yqYHADQRgHz8OIP7ObYsPFS-gmzfkCvEO0a-KKf7zI,513
186
186
  orchestrator/graphql/schemas/helpers.py,sha256=Kpj4kIbmoKKN35bdgUSwQvGUIbeg7VJAVMEq65YS_ik,346
187
- orchestrator/graphql/schemas/process.py,sha256=nvD6Rvr0hnrMINdXF_rQuLF8szKJ7E-SywCFMuZsnlg,4940
187
+ orchestrator/graphql/schemas/process.py,sha256=g3noYh_USfnaK59fnoX2DI5tAf1PhdLMJGI_lA2xX1M,4966
188
188
  orchestrator/graphql/schemas/product.py,sha256=vUCqcjrKBJj-VKSrMYPKzjmmxLMXL7alKTJ8UdUkhTg,4342
189
189
  orchestrator/graphql/schemas/product_block.py,sha256=Qk9cbA6vm7ZPrhdgPHatKRuy6TytBmxSr97McEOxAu8,2860
190
190
  orchestrator/graphql/schemas/resource_type.py,sha256=s5d_FwQXL2-Sc-IDUxTJun5qFQ4zOP4-XcHF9ql-t1g,898
@@ -208,6 +208,7 @@ orchestrator/metrics/init.py,sha256=xBITvDjbNf-iabbBg0tAW8TPj6-wzr_MerOOqgDsoS4,
208
208
  orchestrator/metrics/processes.py,sha256=SyogN5NSuhYoRv2CSUE1So9e8Gkrwa71J6oGLOdODQU,5333
209
209
  orchestrator/metrics/subscriptions.py,sha256=vC1O8VmTq5oJxNrn5CU99Rf8cxzdyhc7tXbZBSAU-O8,3036
210
210
  orchestrator/migrations/README,sha256=heMzebYwlGhnE8_4CWJ4LS74WoEZjBy-S-mIJRxAEKI,39
211
+ orchestrator/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
211
212
  orchestrator/migrations/alembic.ini,sha256=kMoADqhGeubU8xanILNaqm4oixLy9m4ngYtdGpZcc7I,873
212
213
  orchestrator/migrations/env.py,sha256=M_cPoAL2axuuup5fvMy8I_WTPHEw0RbPEHkhZ3QEGoE,3740
213
214
  orchestrator/migrations/helpers.py,sha256=CAGGKhxpmhyKGfYcO-SUCPfMTOCZPfEpkJrcm2MYfcE,47979
@@ -264,9 +265,9 @@ orchestrator/schemas/subscription_descriptions.py,sha256=Ft_jw1U0bf9Z0U8O4OWfLlc
264
265
  orchestrator/schemas/workflow.py,sha256=VqQ9XfV4fVd6MjY0LRRQzWBJHmlPsAamWfTwDx1cZkg,2102
265
266
  orchestrator/services/__init__.py,sha256=GyHNfEFCGKQwRiN6rQmvSRH2iYX7npjMZn97n8XzmLU,571
266
267
  orchestrator/services/fixed_inputs.py,sha256=kyz7s2HLzyDulvcq-ZqefTw1om86COvyvTjz0_5CmgI,876
267
- orchestrator/services/input_state.py,sha256=WfiQrvatPtE1jR8xArE0XamONt1nemTv5iaROgSiJpM,2389
268
+ orchestrator/services/input_state.py,sha256=6BZOpb3cHpO18K-XG-3QUIV9pIM25_ufdODrp5CmXG4,2390
268
269
  orchestrator/services/process_broadcast_thread.py,sha256=D44YbjF8mRqGuznkRUV4SoRn1J0lfy_x1H508GnSVlU,4649
269
- orchestrator/services/processes.py,sha256=JGM9vWbUjvEpy-IpTIgaYaqcTBKMI-CWTY8SJKBf3eI,30153
270
+ orchestrator/services/processes.py,sha256=NfzdtH4eZK_wYuSmFtUX69qDvoeI8J7sJ2fFyY_VYaM,30544
270
271
  orchestrator/services/products.py,sha256=BP4KyE8zO-8z7Trrs5T6zKBOw53S9BfBJnHWI3p6u5Y,1943
271
272
  orchestrator/services/resource_types.py,sha256=_QBy_JOW_X3aSTqH0CuLrq4zBJL0p7Q-UDJUcuK2_qc,884
272
273
  orchestrator/services/settings.py,sha256=HEWfFulgoEDwgfxGEO__QTr5fDiwNBEj1UhAeTAdbLQ,3159
@@ -315,7 +316,7 @@ orchestrator/workflows/tasks/resume_workflows.py,sha256=T3iobSJjVgiupe0rClD34kUZ
315
316
  orchestrator/workflows/tasks/validate_product_type.py,sha256=paG-NAY1bdde3Adt8zItkcBKf5Pxw6f5ngGW6an6dYU,3192
316
317
  orchestrator/workflows/tasks/validate_products.py,sha256=GZJBoFF-WMphS7ghMs2-gqvV2iL1F0POhk0uSNt93n0,8510
317
318
  orchestrator/workflows/translations/en-GB.json,sha256=ST53HxkphFLTMjFHonykDBOZ7-P_KxksktZU3GbxLt0,846
318
- orchestrator_core-4.3.0rc2.dist-info/licenses/LICENSE,sha256=b-aA5OZQuuBATmLKo_mln8CQrDPPhg3ghLzjPjLn4Tg,11409
319
- orchestrator_core-4.3.0rc2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
320
- orchestrator_core-4.3.0rc2.dist-info/METADATA,sha256=aJsbvoK-FPIjx_tH3UJy3KWgj6aLhFwUb7tJvUxq9gQ,5963
321
- orchestrator_core-4.3.0rc2.dist-info/RECORD,,
319
+ orchestrator_core-4.3.1.dist-info/licenses/LICENSE,sha256=b-aA5OZQuuBATmLKo_mln8CQrDPPhg3ghLzjPjLn4Tg,11409
320
+ orchestrator_core-4.3.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
321
+ orchestrator_core-4.3.1.dist-info/METADATA,sha256=1lkABfKjfjXMQaj6Iw3CcmCkunXswkZ0RBLe61A1fAY,5960
322
+ orchestrator_core-4.3.1.dist-info/RECORD,,