Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
85b8502
fix(workflows): support functionApp task type and unknown task fallbacks
JustAnotherNormalDev May 6, 2026
e031d8b
Update cognite/client/data_classes/workflows.py
JustAnotherNormalDev May 6, 2026
ff2e4dd
Workflows: UnknownWorkflowTaskOutput stores task type and handles non…
JustAnotherNormalDev May 6, 2026
345eae0
Workflows: always dict-shaped unknown task dump; function-only asyncC…
JustAnotherNormalDev May 6, 2026
0288a36
Workflows: drop FunctionApp task classes; use unknown param/output fo…
JustAnotherNormalDev May 7, 2026
9705e42
Merge remote-tracking branch 'origin/master' into fix/workflows-funct…
JustAnotherNormalDev May 8, 2026
f34bac0
Workflows: delegate unknown load to _load; drop recursive key convert…
JustAnotherNormalDev May 8, 2026
7ebc261
Merge branch 'master' into fix/workflows-functionapp-task-type
JustAnotherNormalDev May 8, 2026
ed1756a
Merge origin/master into fix/workflows-functionapp-task-type
JustAnotherNormalDev May 8, 2026
1ccc163
Align WorkflowTask.type with ValidTaskType from parameters
JustAnotherNormalDev May 8, 2026
5cfe95b
Merge branch 'fix/workflows-functionapp-task-type' of https://github.…
JustAnotherNormalDev May 8, 2026
4892116
Type WorkflowTaskExecution.task_type as ValidTaskType
JustAnotherNormalDev May 8, 2026
ee0d026
Address review: unknown workflow task params/output dump and load
JustAnotherNormalDev May 8, 2026
0febb29
Skip generic CogniteResource round-trips for UnknownWorkflowTaskParam…
JustAnotherNormalDev May 8, 2026
3a7d59f
Empty commit to retrigger CI
JustAnotherNormalDev May 8, 2026
ebf3a2d
Empty commit to trigger CI
JustAnotherNormalDev May 8, 2026
f2a86ed
Merge branch 'master' into fix/workflows-functionapp-task-type
JustAnotherNormalDev May 8, 2026
5a6db47
Merge origin/master into fix/workflows-functionapp-task-type
JustAnotherNormalDev May 8, 2026
19e70c5
Type UnknownWorkflowTaskOutput as dict; align workflow tests
JustAnotherNormalDev May 8, 2026
a6a6e03
Merge branch 'fix/workflows-functionapp-task-type' of https://github.…
JustAnotherNormalDev May 8, 2026
ebe6eba
Merge origin/master into fix/workflows-functionapp-task-type
JustAnotherNormalDev May 11, 2026
4dd0d13
Workflows: unknown task output/parameters tests and base test pointer
JustAnotherNormalDev May 11, 2026
8f4df5c
Fix UnknownWorkflowTaskParameters init; exclude from json serialize t…
JustAnotherNormalDev May 11, 2026
acd4943
fix(workflows): unknown task parameters dump/load and load_parameters…
JustAnotherNormalDev May 11, 2026
235b0b1
Merge branch 'master' into fix/workflows-functionapp-task-type
JustAnotherNormalDev May 12, 2026
8ce243a
Merge origin/master
JustAnotherNormalDev May 12, 2026
28353ac
chore(workflows): drop functionApp from ValidTaskType literal
JustAnotherNormalDev May 12, 2026
015145c
Merge branch 'fix/workflows-functionapp-task-type' of https://github.…
JustAnotherNormalDev May 12, 2026
63ad6ab
test: restore test_base.py to match master
JustAnotherNormalDev May 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cognite/client/data_classes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@
SubworkflowTaskParameters,
TransformationTaskOutput,
TransformationTaskParameters,
UnknownWorkflowTaskOutput,
UnknownWorkflowTaskParameters,
Workflow,
WorkflowDefinition,
WorkflowDefinitionUpsert,
Expand Down Expand Up @@ -548,6 +550,8 @@
"TransformationUpdate",
"TransformationWrite",
"TransformationWriteList",
"UnknownWorkflowTaskOutput",
"UnknownWorkflowTaskParameters",
"UserProfile",
"UserProfileList",
"Workflow",
Expand Down
73 changes: 70 additions & 3 deletions cognite/client/data_classes/workflows.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import warnings
from abc import ABC, abstractmethod
from collections import UserList
from collections.abc import Collection, Sequence
from copy import deepcopy
from dataclasses import dataclass
from typing import Any, ClassVar, Literal, TypeAlias, cast, final
from zoneinfo import ZoneInfo
Expand Down Expand Up @@ -148,7 +150,14 @@ def as_write(self) -> WorkflowUpsertList:
return WorkflowUpsertList([workflow.as_write() for workflow in self.data])


ValidTaskType = Literal["function", "transformation", "cdf", "dynamic", "subworkflow", "simulation"]
ValidTaskType = Literal[
"function",
"transformation",
"cdf",
"dynamic",
"subworkflow",
"simulation",
]


class WorkflowTaskParameters(CogniteResource, ABC):
Expand Down Expand Up @@ -179,7 +188,7 @@ def load_parameters(cls, data: dict) -> WorkflowTaskParameters:
elif type_ == "simulation":
return SimulationTaskParameters._load(parameters)
else:
raise ValueError(f"Unknown task type: {type_}. Expected {ValidTaskType}")
return UnknownWorkflowTaskParameters(type_, deepcopy(parameters))


class FunctionTaskParameters(WorkflowTaskParameters):
Expand Down Expand Up @@ -257,6 +266,42 @@ def dump(self, camel_case: bool = True) -> dict[str, Any]:
return output


class UnknownWorkflowTaskParameters(WorkflowTaskParameters):
# CogniteResource.load parses strings to a dict before _load; a bare scalar from dump()
# would still parse to a non-dict and fail. Wrapping non-dict payloads keeps JSON/YAML
# round-trips (e.g. test_base) valid while still storing the original value in _parameters.
_OPAQUE_WRAPPER_KEY: ClassVar[str] = "__cogniteSdkUnknownWorkflowTaskParametersOpaque__"

def __init__(self, dynamic_task_type: str | None, parameters: Any) -> None:
self.dynamic_task_type = dynamic_task_type
self._parameters = parameters

@classmethod
def _load(cls, resource: dict[str, Any]) -> Self:
# dump() wraps non-dict _parameters in a one-key dict so load() always receives a dict;
# here we reverse that and store the inner value again (no task type on that path).
if len(resource) == 1 and next(iter(resource), None) == cls._OPAQUE_WRAPPER_KEY:
return cls(None, deepcopy(resource[cls._OPAQUE_WRAPPER_KEY]))
t = resource.get("type", resource.get("taskType"))
return cls(t, deepcopy(resource))

@property
def task_type(self) -> ValidTaskType: # type: ignore[override]
return cast(ValidTaskType, self.dynamic_task_type)

def dump(self, camel_case: bool = True) -> dict[str, Any]:
if not camel_case:
warnings.warn(
"camel_case=False is ignored for UnknownWorkflowTaskParameters.dump(); API payloads use camelCase keys.",
UserWarning,
stacklevel=2,
)
p = deepcopy(self._parameters)
if isinstance(p, dict):
return p
return {self._OPAQUE_WRAPPER_KEY: p}


_SIMULATORS_WARNING = FeaturePreviewWarning(
api_maturity="General Availability", sdk_maturity="alpha", feature_name="Simulators"
)
Expand Down Expand Up @@ -626,7 +671,7 @@ def load_output(cls, data: dict) -> WorkflowTaskOutput:
elif task_type == "simulation":
return SimulationTaskOutput.load(data)
else:
raise ValueError(f"Unknown task type: {task_type}")
return UnknownWorkflowTaskOutput.load(data)

@abstractmethod
def dump(self, camel_case: bool = True) -> dict[str, Any]:
Expand Down Expand Up @@ -664,6 +709,28 @@ def dump(self, camel_case: bool = True) -> dict[str, Any]:
}


class UnknownWorkflowTaskOutput(WorkflowTaskOutput):
def __init__(self, output: Any) -> None:
self._output = output

@classmethod
def load(cls, data: dict[str, Any]) -> Self:
return cls(data.get("output", {}))

@property
def task_type(self) -> ValidTaskType: # type: ignore[override]
return cast(ValidTaskType, "unknown")

def dump(self, camel_case: bool = True) -> Any:
if not camel_case:
warnings.warn(
"camel_case=False is ignored for UnknownWorkflowTaskOutput.dump(); API payloads use camelCase keys.",
UserWarning,
stacklevel=2,
)
return deepcopy(self._output)


class SimulationTaskOutput(WorkflowTaskOutput):
"""
The class represent the output of Simulation execution.
Expand Down
28 changes: 28 additions & 0 deletions tests/tests_unit/test_data_classes/test_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
SimulationTaskParameters,
TransformationTaskOutput,
TransformationTaskParameters,
UnknownWorkflowTaskOutput,
WorkflowDefinition,
WorkflowDefinitionUpsert,
WorkflowExecutionDetailed,
Expand Down Expand Up @@ -245,8 +246,35 @@ class TestWorkflowTask:
},
},
),
(
{
"externalId": "taskFuture",
"type": "futureWorkflowTaskType",
"parameters": {"futureWorkflowTaskType": {"alpha": 1}},
},
),
],
)
def test_serialization(self, raw: dict[str, Any]) -> None:
loaded = WorkflowTask._load(raw)
assert loaded.dump() == raw


class TestWorkflowTaskOutputDispatch:
def test_load_output_unknown_task_type(self) -> None:
data: dict[str, Any] = {"taskType": "customWorkflowOutput", "output": {"customField": 99}}
loaded = WorkflowTaskOutput.load_output(data)
assert isinstance(loaded, UnknownWorkflowTaskOutput)

@pytest.mark.parametrize(
"output_payload",
[
{"customField": 99},
{"otherKey": "value"},
],
)
def test_unknown_output_dump_returns_stored_payload(self, output_payload: dict[str, Any]) -> None:
data: dict[str, Any] = {"taskType": "customWorkflowOutput", "output": output_payload}
loaded = WorkflowTaskOutput.load_output(data)
assert isinstance(loaded, UnknownWorkflowTaskOutput)
assert loaded.dump() == output_payload
Loading