dex-python-sdk 0.0.2__cp311-abi3-win_amd64.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.
Files changed (121) hide show
  1. dex/__init__.py +146 -0
  2. dex/_grpc_errors.py +73 -0
  3. dex/_invocation_context.py +269 -0
  4. dex/_native.pyd +0 -0
  5. dex/_native.pyi +20 -0
  6. dex/_utils.py +18 -0
  7. dex/_value_hydrator.py +223 -0
  8. dex/_value_mapper.py +174 -0
  9. dex/_worker_dispatcher.py +451 -0
  10. dex/_worker_service.py +60 -0
  11. dex/attribute.py +87 -0
  12. dex/blob_cache.py +73 -0
  13. dex/channel.py +165 -0
  14. dex/client.py +715 -0
  15. dex/client_options.py +19 -0
  16. dex/codec.py +286 -0
  17. dex/command_request.py +120 -0
  18. dex/command_results.py +107 -0
  19. dex/communication.py +136 -0
  20. dex/communication_schema.py +54 -0
  21. dex/condition.py +74 -0
  22. dex/context.py +86 -0
  23. dex/data_attributes.py +70 -0
  24. dex/dexpb/__init__.py +1 -0
  25. dex/dexpb/dex_pb2.py +381 -0
  26. dex/dexpb/dex_pb2.pyi +1734 -0
  27. dex/dexpb/dex_pb2_grpc.py +1298 -0
  28. dex/errors.py +109 -0
  29. dex/flow.py +456 -0
  30. dex/flow_config.py +29 -0
  31. dex/flow_info.py +51 -0
  32. dex/flow_options.py +122 -0
  33. dex/object_encoder.py +799 -0
  34. dex/persistence.py +89 -0
  35. dex/persistence_options.py +12 -0
  36. dex/persistence_schema.py +51 -0
  37. dex/py.typed +1 -0
  38. dex/registry.py +204 -0
  39. dex/reset_workflow_type_and_options.py +67 -0
  40. dex/rpc.py +93 -0
  41. dex/runtime_errors.py +81 -0
  42. dex/search_attributes.py +184 -0
  43. dex/state_decision.py +153 -0
  44. dex/state_execution_locals.py +66 -0
  45. dex/state_movement.py +115 -0
  46. dex/state_schema.py +48 -0
  47. dex/step.py +194 -0
  48. dex/step_execution.py +42 -0
  49. dex/stop_workflow_options.py +18 -0
  50. dex/tests/__init__.py +80 -0
  51. dex/tests/dex-service-env/.env +7 -0
  52. dex/tests/dex-service-env/docker-compose-init.sh +44 -0
  53. dex/tests/dex-service-env/docker-compose.yml +97 -0
  54. dex/tests/dex-service-env/dynamicconfig/README.md +39 -0
  55. dex/tests/dex-service-env/dynamicconfig/development-sql.yaml +9 -0
  56. dex/tests/dex-service-env/dynamicconfig/docker.yaml +2 -0
  57. dex/tests/test_abnormal_exit_workflow.py +43 -0
  58. dex/tests/test_basic_workflow.py +70 -0
  59. dex/tests/test_conditional_complete.py +50 -0
  60. dex/tests/test_describe_workflow.py +40 -0
  61. dex/tests/test_empty_data_decodes_properly.py +74 -0
  62. dex/tests/test_internal_channel.py +28 -0
  63. dex/tests/test_internal_channel_with_no_prefix_channel.py +41 -0
  64. dex/tests/test_persistence_data_attributes.py +62 -0
  65. dex/tests/test_persistence_search_attributes.py +127 -0
  66. dex/tests/test_persistence_state_execution_locals.py +38 -0
  67. dex/tests/test_rpc.py +64 -0
  68. dex/tests/test_rpc_with_memo.py +195 -0
  69. dex/tests/test_rpc_with_memo_duplicate_java_tests.py +117 -0
  70. dex/tests/test_signal.py +51 -0
  71. dex/tests/test_skip_wait_until.py +76 -0
  72. dex/tests/test_state_failure_recovery.py +28 -0
  73. dex/tests/test_timer.py +35 -0
  74. dex/tests/test_wait_for_state_execution_completion.py +53 -0
  75. dex/tests/test_workflow_errors.py +87 -0
  76. dex/tests/test_workflow_state_options.py +118 -0
  77. dex/tests/test_workflow_state_options_override.py +44 -0
  78. dex/tests/worker_server.py +64 -0
  79. dex/tests/workflows/abnormal_exit_workflow.py +42 -0
  80. dex/tests/workflows/basic_workflow.py +62 -0
  81. dex/tests/workflows/conditional_complete_workflow.py +95 -0
  82. dex/tests/workflows/describe_workflow.py +46 -0
  83. dex/tests/workflows/empty_data_workflow.py +45 -0
  84. dex/tests/workflows/internal_channel_workflow.py +129 -0
  85. dex/tests/workflows/internal_channel_workflow_with_no_prefix_channel.py +100 -0
  86. dex/tests/workflows/java_duplicate_rpc_memo_workflow.py +276 -0
  87. dex/tests/workflows/persistence_data_attributes_workflow.py +98 -0
  88. dex/tests/workflows/persistence_search_attributes_workflow.py +159 -0
  89. dex/tests/workflows/persistence_state_execution_local_workflow.py +63 -0
  90. dex/tests/workflows/recovery_workflow.py +82 -0
  91. dex/tests/workflows/rpc_memo_workflow.py +231 -0
  92. dex/tests/workflows/rpc_workflow.py +117 -0
  93. dex/tests/workflows/state_options_override_workflow.py +93 -0
  94. dex/tests/workflows/state_options_workflow.py +84 -0
  95. dex/tests/workflows/timer_workflow.py +46 -0
  96. dex/tests/workflows/wait_for_state_with_state_execution_id_workflow.py +70 -0
  97. dex/tests/workflows/wait_for_state_with_wait_for_key_workflow.py +71 -0
  98. dex/tests/workflows/wait_internal_channel_workflow.py +47 -0
  99. dex/tests/workflows/wait_signal_workflow.py +147 -0
  100. dex/timer.py +21 -0
  101. dex/type_store.py +99 -0
  102. dex/unregistered_client.py +585 -0
  103. dex/utils/__init__.py +3 -0
  104. dex/utils/dex_typing.py +25 -0
  105. dex/utils/persistence_utils.py +32 -0
  106. dex/wait.py +49 -0
  107. dex/worker.py +121 -0
  108. dex/worker_options.py +22 -0
  109. dex/worker_service.py +432 -0
  110. dex/workflow.py +79 -0
  111. dex/workflow_context.py +44 -0
  112. dex/workflow_info.py +16 -0
  113. dex/workflow_options.py +74 -0
  114. dex/workflow_state.py +123 -0
  115. dex/workflow_state_options.py +154 -0
  116. dex_python_sdk-0.0.2.dist-info/METADATA +202 -0
  117. dex_python_sdk-0.0.2.dist-info/RECORD +121 -0
  118. dex_python_sdk-0.0.2.dist-info/WHEEL +4 -0
  119. dex_python_sdk-0.0.2.dist-info/licenses/LEGACY_NOTICES.md +61 -0
  120. dex_python_sdk-0.0.2.dist-info/licenses/LICENSE +192 -0
  121. dex_python_sdk-0.0.2.dist-info/sboms/dex-blob-cache-python.cyclonedx.json +2406 -0
dex/step.py ADDED
@@ -0,0 +1,194 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from __future__ import annotations
12
+
13
+ from abc import ABC, abstractmethod
14
+ from dataclasses import dataclass, replace
15
+ from datetime import timedelta
16
+ from enum import Enum
17
+ from typing import Any, Generic, Iterator, TypeVar
18
+
19
+ from dex.attribute import AttributeLock
20
+ from dex.channel import Channel, ChannelMap
21
+ from dex.context import Context
22
+ from dex.wait import Wait
23
+
24
+ InputT = TypeVar("InputT")
25
+ StartT = TypeVar("StartT")
26
+
27
+
28
+ class StepDurability(Enum):
29
+ DEFAULT = "default"
30
+ SYNC = "sync"
31
+ ASYNC = "async"
32
+
33
+
34
+ class WaitForFailurePolicy(Enum):
35
+ FAIL_FLOW = "fail_flow"
36
+ PROCEED = "proceed"
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class RetryPolicy:
41
+ initial_interval: timedelta | None = None
42
+ backoff_coefficient: float = 0.0
43
+ maximum_interval: timedelta | None = None
44
+ maximum_attempts: int = 0
45
+ total_duration: timedelta | None = None
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class StepOptions:
50
+ wait_for_method_timeout: timedelta | None = None
51
+ execute_method_timeout: timedelta | None = None
52
+ wait_for_retry: RetryPolicy | None = None
53
+ execute_retry: RetryPolicy | None = None
54
+ wait_for_failure: WaitForFailurePolicy = WaitForFailurePolicy.FAIL_FLOW
55
+ wait_for_durability: StepDurability = StepDurability.DEFAULT
56
+ execute_durability: StepDurability = StepDurability.DEFAULT
57
+ wait_for_lock_attributes: tuple[AttributeLock, ...] = ()
58
+ execute_lock_attributes: tuple[AttributeLock, ...] = ()
59
+ _execute_failure_target: Step[Any] | None = None
60
+ _execute_failure_options: StepOptions | None = None
61
+
62
+ def on_execute_failure_proceed_to(
63
+ self,
64
+ step: Step[Any],
65
+ options: StepOptions | None = None,
66
+ ) -> StepOptions:
67
+ return replace(
68
+ self,
69
+ _execute_failure_target=step,
70
+ _execute_failure_options=options,
71
+ )
72
+
73
+
74
+ class Step(Generic[InputT], ABC):
75
+ @abstractmethod
76
+ def execute(
77
+ self,
78
+ context: Context,
79
+ input: InputT,
80
+ ) -> StepDecision:
81
+ raise NotImplementedError
82
+
83
+ def wait_for(self, context: Context, input: InputT) -> Wait:
84
+ del context, input
85
+ raise RuntimeError("framework must skip the default wait_for")
86
+
87
+ def get_step_type(self) -> str:
88
+ return type(self).__name__
89
+
90
+ def get_step_options(self) -> StepOptions | None:
91
+ return None
92
+
93
+
94
+ @dataclass(frozen=True)
95
+ class _StepDef:
96
+ step: Step[Any]
97
+ is_start_step: bool
98
+
99
+
100
+ @dataclass(frozen=True)
101
+ class StepList(Generic[StartT]):
102
+ _definitions: tuple[_StepDef, ...]
103
+
104
+ @classmethod
105
+ def empty(cls) -> StepList[StartT]:
106
+ return cls(())
107
+
108
+ @staticmethod
109
+ def start_step(step: Step[StartT]) -> StepList[StartT]:
110
+ return StepList((_StepDef(step, True),))
111
+
112
+ @classmethod
113
+ def without_start_step(cls, *steps: Step[Any]) -> StepList[StartT]:
114
+ return cls(tuple(_StepDef(step, False) for step in steps))
115
+
116
+ def other_steps(self, *steps: Step[Any]) -> StepList[StartT]:
117
+ return StepList(
118
+ self._definitions + tuple(_StepDef(step, False) for step in steps)
119
+ )
120
+
121
+ def __iter__(self) -> Iterator[_StepDef]:
122
+ return iter(self._definitions)
123
+
124
+
125
+ @dataclass(frozen=True)
126
+ class StepMovement(Generic[InputT]):
127
+ step: Step[InputT]
128
+ input: InputT
129
+ options: StepOptions | None = None
130
+
131
+ @staticmethod
132
+ def of(
133
+ step: Step[InputT],
134
+ input: InputT,
135
+ *,
136
+ options: StepOptions | None = None,
137
+ ) -> StepMovement[InputT]:
138
+ return StepMovement(step, input, options)
139
+
140
+
141
+ class DecisionKind(Enum):
142
+ NEXT = "next"
143
+ GRACEFUL_COMPLETE = "graceful_complete"
144
+ FORCE_COMPLETE = "force_complete"
145
+ FORCE_COMPLETE_IF_CHANNELS_EMPTY = "force_complete_if_channels_empty"
146
+ FORCE_FAIL = "force_fail"
147
+ DEAD_END = "dead_end"
148
+
149
+
150
+ @dataclass(frozen=True)
151
+ class StepDecision:
152
+ kind: DecisionKind
153
+ movements: tuple[StepMovement[Any], ...] = ()
154
+ output: object | None = None
155
+ reason: str = ""
156
+ empty_channels: tuple[Channel[Any] | ChannelMap[Any], ...] = ()
157
+ fallback: StepMovement[Any] | None = None
158
+
159
+
160
+ def go_to(step: Step[InputT], input: InputT) -> StepDecision:
161
+ return go_to_multi(StepMovement.of(step, input))
162
+
163
+
164
+ def go_to_multi(*movements: StepMovement[Any]) -> StepDecision:
165
+ return StepDecision(DecisionKind.NEXT, movements=movements)
166
+
167
+
168
+ def graceful_complete(output: object | None = None) -> StepDecision:
169
+ return StepDecision(DecisionKind.GRACEFUL_COMPLETE, output=output)
170
+
171
+
172
+ def force_complete(output: object | None = None) -> StepDecision:
173
+ return StepDecision(DecisionKind.FORCE_COMPLETE, output=output)
174
+
175
+
176
+ def force_complete_when_channels_empty(
177
+ output: object,
178
+ fallback: StepMovement[Any],
179
+ *channels: Channel[Any] | ChannelMap[Any],
180
+ ) -> StepDecision:
181
+ return StepDecision(
182
+ DecisionKind.FORCE_COMPLETE_IF_CHANNELS_EMPTY,
183
+ output=output,
184
+ empty_channels=channels,
185
+ fallback=fallback,
186
+ )
187
+
188
+
189
+ def force_fail(reason: str) -> StepDecision:
190
+ return StepDecision(DecisionKind.FORCE_FAIL, reason=reason)
191
+
192
+
193
+ def dead_end() -> StepDecision:
194
+ return StepDecision(DecisionKind.DEAD_END)
dex/step_execution.py ADDED
@@ -0,0 +1,42 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+
15
+ from dex._utils import require_name
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class StepExecutionId:
20
+ step_type: str
21
+ number: int = 1
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class TimerId:
26
+ condition_id: str | None = None
27
+ condition_index: int | None = None
28
+
29
+ def __post_init__(self) -> None:
30
+ if (self.condition_id is None) == (self.condition_index is None):
31
+ raise ValueError("TimerId requires exactly one selector")
32
+ if self.condition_index is not None and self.condition_index < 0:
33
+ raise ValueError("timer condition index must not be negative")
34
+
35
+ @staticmethod
36
+ def by_condition_id(condition_id: str) -> TimerId:
37
+ require_name(condition_id)
38
+ return TimerId(condition_id=condition_id)
39
+
40
+ @staticmethod
41
+ def by_condition_index(condition_index: int) -> TimerId:
42
+ return TimerId(condition_index=condition_index)
@@ -0,0 +1,18 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dataclasses import dataclass
12
+
13
+ from dex.dex_api.models import WorkflowStopType
14
+
15
+ @dataclass
16
+ class StopWorkflowOptions:
17
+ workflow_stop_type: WorkflowStopType
18
+ reason: str
dex/tests/__init__.py ADDED
@@ -0,0 +1,80 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dex.registry import Registry
12
+ from dex.tests.workflows.abnormal_exit_workflow import AbnormalExitWorkflow
13
+ from dex.tests.workflows.basic_workflow import BasicWorkflow
14
+ from dex.tests.workflows.conditional_complete_workflow import (
15
+ ConditionalCompleteWorkflow,
16
+ )
17
+ from dex.tests.workflows.describe_workflow import DescribeWorkflow
18
+ from dex.tests.workflows.empty_data_workflow import EmptyDataWorkflow
19
+ from dex.tests.workflows.internal_channel_workflow import InternalChannelWorkflow
20
+ from dex.tests.workflows.internal_channel_workflow_with_no_prefix_channel import (
21
+ InternalChannelWorkflowWithNoPrefixChannel,
22
+ )
23
+ from dex.tests.workflows.java_duplicate_rpc_memo_workflow import (
24
+ JavaDuplicateRpcMemoWorkflow,
25
+ )
26
+ from dex.tests.workflows.persistence_data_attributes_workflow import (
27
+ PersistenceDataAttributesWorkflow,
28
+ )
29
+ from dex.tests.workflows.persistence_search_attributes_workflow import (
30
+ PersistenceSearchAttributesWorkflow,
31
+ )
32
+ from dex.tests.workflows.persistence_state_execution_local_workflow import (
33
+ PersistenceStateExecutionLocalWorkflow,
34
+ )
35
+ from dex.tests.workflows.recovery_workflow import RecoveryWorkflow
36
+ from dex.tests.workflows.rpc_memo_workflow import RpcMemoWorkflow
37
+ from dex.tests.workflows.rpc_workflow import RPCWorkflow
38
+ from dex.tests.workflows.state_options_override_workflow import (
39
+ StateOptionsOverrideWorkflow,
40
+ )
41
+ from dex.tests.workflows.state_options_workflow import (
42
+ StateOptionsWorkflow1,
43
+ StateOptionsWorkflow2,
44
+ )
45
+ from dex.tests.workflows.timer_workflow import TimerWorkflow
46
+ from dex.tests.workflows.wait_for_state_with_state_execution_id_workflow import (
47
+ WaitForStateWithStateExecutionIdWorkflow,
48
+ )
49
+ from dex.tests.workflows.wait_for_state_with_wait_for_key_workflow import (
50
+ WaitForStateWithWaitForKeyWorkflow,
51
+ )
52
+ from dex.tests.workflows.wait_internal_channel_workflow import (
53
+ WaitInternalChannelWorkflow,
54
+ )
55
+ from dex.tests.workflows.wait_signal_workflow import WaitSignalWorkflow
56
+
57
+ registry = Registry()
58
+
59
+ registry.add_workflow(AbnormalExitWorkflow())
60
+ registry.add_workflow(BasicWorkflow())
61
+ registry.add_workflow(ConditionalCompleteWorkflow())
62
+ registry.add_workflow(DescribeWorkflow())
63
+ registry.add_workflow(EmptyDataWorkflow())
64
+ registry.add_workflow(InternalChannelWorkflow())
65
+ registry.add_workflow(InternalChannelWorkflowWithNoPrefixChannel())
66
+ registry.add_workflow(JavaDuplicateRpcMemoWorkflow())
67
+ registry.add_workflow(PersistenceDataAttributesWorkflow())
68
+ registry.add_workflow(PersistenceSearchAttributesWorkflow())
69
+ registry.add_workflow(PersistenceStateExecutionLocalWorkflow())
70
+ registry.add_workflow(RecoveryWorkflow())
71
+ registry.add_workflow(RpcMemoWorkflow())
72
+ registry.add_workflow(RPCWorkflow())
73
+ registry.add_workflow(StateOptionsOverrideWorkflow())
74
+ registry.add_workflow(StateOptionsWorkflow1())
75
+ registry.add_workflow(StateOptionsWorkflow2())
76
+ registry.add_workflow(TimerWorkflow())
77
+ registry.add_workflow(WaitForStateWithStateExecutionIdWorkflow())
78
+ registry.add_workflow(WaitForStateWithWaitForKeyWorkflow())
79
+ registry.add_workflow(WaitInternalChannelWorkflow())
80
+ registry.add_workflow(WaitSignalWorkflow())
@@ -0,0 +1,7 @@
1
+ CASSANDRA_VERSION=3.11.9
2
+ ELASTICSEARCH_VERSION=7.17.27
3
+ MYSQL_VERSION=8
4
+ POSTGRESQL_VERSION=13
5
+ TEMPORAL_VERSION=1.25
6
+ TEMPORAL_UI_VERSION=2.31.2
7
+ TEMPORAL_ADMIN_TOOLS_VERSION=1.25.2-tctl-1.18.1-cli-1.1.1
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+
3
+ checkExists () {
4
+ # see https://github.com/temporalio/temporal/issues/4160
5
+ if temporal operator search-attribute list | grep -q "$1"; then
6
+ return 0
7
+ else
8
+ return 1
9
+ fi
10
+ }
11
+
12
+ echo "now trying to register Dex system search attributes..."
13
+ for run in {1..120}; do
14
+ sleep 1
15
+ temporal operator search-attribute create --name DexWorkflowType --type Keyword
16
+ sleep 0.1
17
+ temporal operator search-attribute create --name DexGlobalWorkflowVersion --type Int
18
+ sleep 0.1
19
+ temporal operator search-attribute create --name DexExecutingStateIds --type KeywordList
20
+ sleep 0.1
21
+ temporal operator search-attribute create --name CustomKeywordField --type Keyword
22
+ sleep 0.1
23
+ temporal operator search-attribute create --name CustomIntField --type Int
24
+ sleep 0.1
25
+ temporal operator search-attribute create --name CustomBoolField --type Bool
26
+ sleep 0.1
27
+ temporal operator search-attribute create --name CustomDoubleField --type Double
28
+ sleep 0.1
29
+ temporal operator search-attribute create --name CustomDatetimeField --type Datetime
30
+ sleep 0.1
31
+ temporal operator search-attribute create --name CustomStringField --type Text
32
+ sleep 0.1
33
+ temporal operator search-attribute create --name CustomKeywordArrayField --type KeywordList
34
+ sleep 0.1
35
+
36
+ if checkExists "DexWorkflowType" && checkExists "DexGlobalWorkflowVersion" && checkExists "DexExecutingStateIds" && checkExists "CustomKeywordField" && checkExists "CustomIntField" && checkExists "CustomBoolField" && checkExists "CustomDoubleField" && checkExists "CustomDatetimeField" && checkExists "CustomStringField" && checkExists "CustomKeywordArrayField"; then
37
+ echo "All search attributes are registered"
38
+ break
39
+ fi
40
+
41
+ done
42
+
43
+
44
+ tail -f /dev/null
@@ -0,0 +1,97 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ version: '3.9'
12
+
13
+ # see .env file for the default value of the environment variables
14
+ services:
15
+ elasticsearch:
16
+ container_name: temporal-elasticsearch-dex
17
+ environment:
18
+ - cluster.routing.allocation.disk.threshold_enabled=true
19
+ - cluster.routing.allocation.disk.watermark.low=512mb
20
+ - cluster.routing.allocation.disk.watermark.high=256mb
21
+ - cluster.routing.allocation.disk.watermark.flood_stage=128mb
22
+ - discovery.type=single-node
23
+ - ES_JAVA_OPTS=-Xms512m -Xmx512m
24
+ - xpack.security.enabled=false
25
+ image: elasticsearch:${ELASTICSEARCH_VERSION}
26
+ networks:
27
+ - temporal-network
28
+ expose:
29
+ - 9200
30
+ postgresql:
31
+ container_name: temporal-postgresql-dex
32
+ environment:
33
+ POSTGRES_PASSWORD: temporal
34
+ POSTGRES_USER: temporal
35
+ image: postgres:${POSTGRESQL_VERSION}
36
+ networks:
37
+ - temporal-network
38
+ expose:
39
+ - 5432
40
+ temporal:
41
+ container_name: temporal-dex
42
+ depends_on:
43
+ - postgresql
44
+ - elasticsearch
45
+ environment:
46
+ - DB=postgres12
47
+ - DB_PORT=5432
48
+ - POSTGRES_USER=temporal
49
+ - POSTGRES_PWD=temporal
50
+ - POSTGRES_SEEDS=postgresql
51
+ - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml
52
+ - ENABLE_ES=true
53
+ - ES_SEEDS=elasticsearch
54
+ - ES_VERSION=v7
55
+ image: temporalio/auto-setup:${TEMPORAL_VERSION}
56
+ networks:
57
+ - temporal-network
58
+ ports:
59
+ - 7233:7233
60
+ labels:
61
+ kompose.volume.type: configMap
62
+ volumes:
63
+ - ./dynamicconfig:/etc/temporal/config/dynamicconfig
64
+ temporal-admin-tools:
65
+ container_name: temporal-admin-tools-dex
66
+ depends_on:
67
+ - temporal
68
+ environment:
69
+ - TEMPORAL_ADDRESS=temporal:7233
70
+ - TEMPORAL_CLI_ADDRESS=temporal:7233
71
+ image: temporalio/admin-tools:${TEMPORAL_ADMIN_TOOLS_VERSION}
72
+ networks:
73
+ - temporal-network
74
+ stdin_open: true
75
+ tty: true
76
+ volumes:
77
+ - ./docker-compose-init.sh:/etc/temporal/init.sh
78
+ entrypoint: sh -c "/etc/temporal/init.sh"
79
+ dex-server:
80
+ container_name: dex-server
81
+ image: ${DEX_SERVER_IMAGE:-superdurable/dex-server:latest}
82
+ environment:
83
+ # Replace localhost/127.0.0.1 worker URLs so the container can reach the host worker.
84
+ # host.docker.internal works on Docker Desktop and on Linux with extra_hosts below.
85
+ - AUTO_FIX_WORKER_URL=host.docker.internal
86
+ networks:
87
+ - temporal-network
88
+ ports:
89
+ - 8801:8801
90
+ depends_on:
91
+ - temporal
92
+ extra_hosts:
93
+ - host.docker.internal:host-gateway
94
+ networks:
95
+ temporal-network:
96
+ driver: bridge
97
+ name: temporal-network
@@ -0,0 +1,39 @@
1
+ Use `docker.yaml` file to override the default dynamic config value (they are specified
2
+ when creating the service config).
3
+
4
+ Each key can have zero or more values and each value can have zero or more
5
+ constraints. There are only three types of constraint:
6
+ 1. `namespace`: `string`
7
+ 2. `taskQueueName`: `string`
8
+ 3. `taskType`: `int` (`1`:`Workflow`, `2`:`Activity`)
9
+ A value will be selected and returned if all its has exactly the same constraints
10
+ as the ones specified in query filters (including the number of constraints).
11
+
12
+ Please use the following format:
13
+ ```
14
+ testGetBoolPropertyKey:
15
+ - value: false
16
+ - value: true
17
+ constraints:
18
+ namespace: "global-samples-namespace"
19
+ - value: false
20
+ constraints:
21
+ namespace: "samples-namespace"
22
+ testGetDurationPropertyKey:
23
+ - value: "1m"
24
+ constraints:
25
+ namespace: "samples-namespace"
26
+ taskQueueName: "longIdleTimeTaskqueue"
27
+ testGetFloat64PropertyKey:
28
+ - value: 12.0
29
+ constraints:
30
+ namespace: "samples-namespace"
31
+ testGetMapPropertyKey:
32
+ - value:
33
+ key1: 1
34
+ key2: "value 2"
35
+ key3:
36
+ - false
37
+ - key4: true
38
+ key5: 2.0
39
+ ```
@@ -0,0 +1,9 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ limit.maxIDLength:
5
+ - value: 255
6
+ constraints: {}
7
+ system.forceSearchAttributesCacheRefreshOnRead:
8
+ - value: true # Dev setup only. Please don't turn this on in production.
9
+ constraints: {}
@@ -0,0 +1,2 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
@@ -0,0 +1,43 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.errors import WorkflowFailed
17
+ from dex.dex_api.models.id_reuse_policy import IDReusePolicy
18
+ from dex.tests.worker_server import registry
19
+ from dex.tests.workflows.abnormal_exit_workflow import AbnormalExitWorkflow
20
+ from dex.tests.workflows.basic_workflow import BasicWorkflow
21
+ from dex.workflow_options import WorkflowOptions
22
+
23
+ class TestAbnormalWorkflow(unittest.TestCase):
24
+ @classmethod
25
+ def setUpClass(cls):
26
+ cls.client = Client(registry)
27
+
28
+ def test_abnormal_exit_workflow(self):
29
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
30
+ start_options = WorkflowOptions(
31
+ workflow_id_reuse_policy=IDReusePolicy.ALLOW_IF_PREVIOUS_EXITS_ABNORMALLY
32
+ )
33
+
34
+ self.client.start_workflow(
35
+ AbnormalExitWorkflow, wf_id, 100, "input", start_options
36
+ )
37
+ with self.assertRaises(WorkflowFailed):
38
+ self.client.wait_for_workflow_completion(wf_id, str)
39
+
40
+ # Starting a workflow with the same ID should be allowed since the previous failed abnormally
41
+ self.client.start_workflow(BasicWorkflow, wf_id, 100, "input", start_options)
42
+ res = self.client.wait_for_workflow_completion(wf_id, str)
43
+ assert res == "done"
@@ -0,0 +1,70 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ import inspect
12
+ import time
13
+ import unittest
14
+
15
+ from dex.client import Client
16
+ from dex.errors import WorkflowAlreadyStartedError
17
+ from dex.dex_api.models import WorkflowAlreadyStartedOptions
18
+ from dex.tests.worker_server import registry
19
+ from dex.tests.workflows.basic_workflow import BasicWorkflow
20
+ from dex.workflow_options import WorkflowOptions
21
+
22
+ class TestWorkflowErrors(unittest.TestCase):
23
+ @classmethod
24
+ def setUpClass(cls):
25
+ cls.client = Client(registry)
26
+
27
+ def test_basic_workflow(self):
28
+ original_request_id = "1"
29
+ later_request_id = "2"
30
+
31
+ wf_id = f"{inspect.currentframe().f_code.co_name}-{time.time_ns()}"
32
+
33
+ workflow_already_started_options_1 = WorkflowAlreadyStartedOptions(
34
+ ignore_already_started_error=True
35
+ )
36
+ workflow_already_started_options_1.request_id = original_request_id
37
+
38
+ start_options_1 = WorkflowOptions()
39
+ start_options_1.workflow_already_started_options = (
40
+ workflow_already_started_options_1
41
+ )
42
+
43
+ wf_run_id = self.client.start_workflow(
44
+ BasicWorkflow, wf_id, 100, "input", start_options_1
45
+ )
46
+ assert wf_run_id
47
+
48
+ wf_run_id = self.client.start_workflow(
49
+ BasicWorkflow, wf_id, 100, "input", start_options_1
50
+ )
51
+ assert wf_run_id
52
+
53
+ workflow_already_started_options_2 = WorkflowAlreadyStartedOptions(
54
+ ignore_already_started_error=True
55
+ )
56
+ workflow_already_started_options_2.request_id = later_request_id
57
+
58
+ start_options_2 = WorkflowOptions()
59
+ start_options_2.workflow_already_started_option = (
60
+ workflow_already_started_options_2
61
+ )
62
+
63
+ with self.assertRaises(WorkflowAlreadyStartedError):
64
+ wf_run_id = self.client.start_workflow(
65
+ BasicWorkflow, wf_id, 100, "input", start_options_2
66
+ )
67
+ assert wf_run_id
68
+
69
+ res = self.client.wait_for_workflow_completion(wf_id, str)
70
+ assert res == "done"