Run black on moto & test directories.
This commit is contained in:
parent
c820395dbf
commit
96e5b1993d
507 changed files with 52541 additions and 47814 deletions
|
|
@ -23,14 +23,10 @@ from .workflow_type import WorkflowType # noqa
|
|||
from .workflow_execution import WorkflowExecution # noqa
|
||||
from time import sleep
|
||||
|
||||
KNOWN_SWF_TYPES = {
|
||||
"activity": ActivityType,
|
||||
"workflow": WorkflowType,
|
||||
}
|
||||
KNOWN_SWF_TYPES = {"activity": ActivityType, "workflow": WorkflowType}
|
||||
|
||||
|
||||
class SWFBackend(BaseBackend):
|
||||
|
||||
def __init__(self, region_name):
|
||||
self.region_name = region_name
|
||||
self.domains = []
|
||||
|
|
@ -55,46 +51,53 @@ class SWFBackend(BaseBackend):
|
|||
wfe._process_timeouts()
|
||||
|
||||
def list_domains(self, status, reverse_order=None):
|
||||
domains = [domain for domain in self.domains
|
||||
if domain.status == status]
|
||||
domains = [domain for domain in self.domains if domain.status == status]
|
||||
domains = sorted(domains, key=lambda domain: domain.name)
|
||||
if reverse_order:
|
||||
domains = reversed(domains)
|
||||
return domains
|
||||
|
||||
def list_open_workflow_executions(self, domain_name, maximum_page_size,
|
||||
tag_filter, reverse_order, **kwargs):
|
||||
def list_open_workflow_executions(
|
||||
self, domain_name, maximum_page_size, tag_filter, reverse_order, **kwargs
|
||||
):
|
||||
self._process_timeouts()
|
||||
domain = self._get_domain(domain_name)
|
||||
if domain.status == "DEPRECATED":
|
||||
raise SWFDomainDeprecatedFault(domain_name)
|
||||
open_wfes = [
|
||||
wfe for wfe in domain.workflow_executions
|
||||
if wfe.execution_status == 'OPEN'
|
||||
wfe for wfe in domain.workflow_executions if wfe.execution_status == "OPEN"
|
||||
]
|
||||
|
||||
if tag_filter:
|
||||
for open_wfe in open_wfes:
|
||||
if tag_filter['tag'] not in open_wfe.tag_list:
|
||||
if tag_filter["tag"] not in open_wfe.tag_list:
|
||||
open_wfes.remove(open_wfe)
|
||||
if reverse_order:
|
||||
open_wfes = reversed(open_wfes)
|
||||
return open_wfes[0:maximum_page_size]
|
||||
|
||||
def list_closed_workflow_executions(self, domain_name, close_time_filter,
|
||||
tag_filter, close_status_filter, maximum_page_size, reverse_order,
|
||||
**kwargs):
|
||||
def list_closed_workflow_executions(
|
||||
self,
|
||||
domain_name,
|
||||
close_time_filter,
|
||||
tag_filter,
|
||||
close_status_filter,
|
||||
maximum_page_size,
|
||||
reverse_order,
|
||||
**kwargs
|
||||
):
|
||||
self._process_timeouts()
|
||||
domain = self._get_domain(domain_name)
|
||||
if domain.status == "DEPRECATED":
|
||||
raise SWFDomainDeprecatedFault(domain_name)
|
||||
closed_wfes = [
|
||||
wfe for wfe in domain.workflow_executions
|
||||
if wfe.execution_status == 'CLOSED'
|
||||
wfe
|
||||
for wfe in domain.workflow_executions
|
||||
if wfe.execution_status == "CLOSED"
|
||||
]
|
||||
if tag_filter:
|
||||
for closed_wfe in closed_wfes:
|
||||
if tag_filter['tag'] not in closed_wfe.tag_list:
|
||||
if tag_filter["tag"] not in closed_wfe.tag_list:
|
||||
closed_wfes.remove(closed_wfe)
|
||||
if close_status_filter:
|
||||
for closed_wfe in closed_wfes:
|
||||
|
|
@ -104,12 +107,12 @@ class SWFBackend(BaseBackend):
|
|||
closed_wfes = reversed(closed_wfes)
|
||||
return closed_wfes[0:maximum_page_size]
|
||||
|
||||
def register_domain(self, name, workflow_execution_retention_period_in_days,
|
||||
description=None):
|
||||
def register_domain(
|
||||
self, name, workflow_execution_retention_period_in_days, description=None
|
||||
):
|
||||
if self._get_domain(name, ignore_empty=True):
|
||||
raise SWFDomainAlreadyExistsFault(name)
|
||||
domain = Domain(name, workflow_execution_retention_period_in_days,
|
||||
description)
|
||||
domain = Domain(name, workflow_execution_retention_period_in_days, description)
|
||||
self.domains.append(domain)
|
||||
|
||||
def deprecate_domain(self, name):
|
||||
|
|
@ -149,15 +152,23 @@ class SWFBackend(BaseBackend):
|
|||
domain = self._get_domain(domain_name)
|
||||
return domain.get_type(kind, name, version)
|
||||
|
||||
def start_workflow_execution(self, domain_name, workflow_id,
|
||||
workflow_name, workflow_version,
|
||||
tag_list=None, input=None, **kwargs):
|
||||
def start_workflow_execution(
|
||||
self,
|
||||
domain_name,
|
||||
workflow_id,
|
||||
workflow_name,
|
||||
workflow_version,
|
||||
tag_list=None,
|
||||
input=None,
|
||||
**kwargs
|
||||
):
|
||||
domain = self._get_domain(domain_name)
|
||||
wf_type = domain.get_type("workflow", workflow_name, workflow_version)
|
||||
if wf_type.status == "DEPRECATED":
|
||||
raise SWFTypeDeprecatedFault(wf_type)
|
||||
wfe = WorkflowExecution(domain, wf_type, workflow_id,
|
||||
tag_list=tag_list, input=input, **kwargs)
|
||||
wfe = WorkflowExecution(
|
||||
domain, wf_type, workflow_id, tag_list=tag_list, input=input, **kwargs
|
||||
)
|
||||
domain.add_workflow_execution(wfe)
|
||||
wfe.start()
|
||||
|
||||
|
|
@ -213,9 +224,9 @@ class SWFBackend(BaseBackend):
|
|||
count += wfe.open_counts["openDecisionTasks"]
|
||||
return count
|
||||
|
||||
def respond_decision_task_completed(self, task_token,
|
||||
decisions=None,
|
||||
execution_context=None):
|
||||
def respond_decision_task_completed(
|
||||
self, task_token, decisions=None, execution_context=None
|
||||
):
|
||||
# process timeouts on all objects
|
||||
self._process_timeouts()
|
||||
# let's find decision task
|
||||
|
|
@ -244,14 +255,15 @@ class SWFBackend(BaseBackend):
|
|||
"execution",
|
||||
"WorkflowExecution=[workflowId={0}, runId={1}]".format(
|
||||
wfe.workflow_id, wfe.run_id
|
||||
)
|
||||
),
|
||||
)
|
||||
# decision task found, but already completed
|
||||
if decision_task.state != "STARTED":
|
||||
if decision_task.state == "COMPLETED":
|
||||
raise SWFUnknownResourceFault(
|
||||
"decision task, scheduledEventId = {0}".format(
|
||||
decision_task.scheduled_event_id)
|
||||
decision_task.scheduled_event_id
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
|
|
@ -263,9 +275,11 @@ class SWFBackend(BaseBackend):
|
|||
# everything's good
|
||||
if decision_task:
|
||||
wfe = decision_task.workflow_execution
|
||||
wfe.complete_decision_task(decision_task.task_token,
|
||||
decisions=decisions,
|
||||
execution_context=execution_context)
|
||||
wfe.complete_decision_task(
|
||||
decision_task.task_token,
|
||||
decisions=decisions,
|
||||
execution_context=execution_context,
|
||||
)
|
||||
|
||||
def poll_for_activity_task(self, domain_name, task_list, identity=None):
|
||||
# process timeouts on all objects
|
||||
|
|
@ -308,8 +322,7 @@ class SWFBackend(BaseBackend):
|
|||
count = 0
|
||||
for _task_list, tasks in domain.activity_task_lists.items():
|
||||
if _task_list == task_list:
|
||||
pending = [t for t in tasks if t.state in [
|
||||
"SCHEDULED", "STARTED"]]
|
||||
pending = [t for t in tasks if t.state in ["SCHEDULED", "STARTED"]]
|
||||
count += len(pending)
|
||||
return count
|
||||
|
||||
|
|
@ -333,14 +346,15 @@ class SWFBackend(BaseBackend):
|
|||
"execution",
|
||||
"WorkflowExecution=[workflowId={0}, runId={1}]".format(
|
||||
wfe.workflow_id, wfe.run_id
|
||||
)
|
||||
),
|
||||
)
|
||||
# activity task found, but already completed
|
||||
if activity_task.state != "STARTED":
|
||||
if activity_task.state == "COMPLETED":
|
||||
raise SWFUnknownResourceFault(
|
||||
"activity, scheduledEventId = {0}".format(
|
||||
activity_task.scheduled_event_id)
|
||||
activity_task.scheduled_event_id
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
|
|
@ -364,18 +378,24 @@ class SWFBackend(BaseBackend):
|
|||
self._process_timeouts()
|
||||
activity_task = self._find_activity_task_from_token(task_token)
|
||||
wfe = activity_task.workflow_execution
|
||||
wfe.fail_activity_task(activity_task.task_token,
|
||||
reason=reason, details=details)
|
||||
wfe.fail_activity_task(activity_task.task_token, reason=reason, details=details)
|
||||
|
||||
def terminate_workflow_execution(self, domain_name, workflow_id, child_policy=None,
|
||||
details=None, reason=None, run_id=None):
|
||||
def terminate_workflow_execution(
|
||||
self,
|
||||
domain_name,
|
||||
workflow_id,
|
||||
child_policy=None,
|
||||
details=None,
|
||||
reason=None,
|
||||
run_id=None,
|
||||
):
|
||||
# process timeouts on all objects
|
||||
self._process_timeouts()
|
||||
domain = self._get_domain(domain_name)
|
||||
wfe = domain.get_workflow_execution(
|
||||
workflow_id, run_id=run_id, raise_if_closed=True)
|
||||
wfe.terminate(child_policy=child_policy,
|
||||
details=details, reason=reason)
|
||||
workflow_id, run_id=run_id, raise_if_closed=True
|
||||
)
|
||||
wfe.terminate(child_policy=child_policy, details=details, reason=reason)
|
||||
|
||||
def record_activity_task_heartbeat(self, task_token, details=None):
|
||||
# process timeouts on all objects
|
||||
|
|
@ -385,12 +405,15 @@ class SWFBackend(BaseBackend):
|
|||
if details:
|
||||
activity_task.details = details
|
||||
|
||||
def signal_workflow_execution(self, domain_name, signal_name, workflow_id, input=None, run_id=None):
|
||||
def signal_workflow_execution(
|
||||
self, domain_name, signal_name, workflow_id, input=None, run_id=None
|
||||
):
|
||||
# process timeouts on all objects
|
||||
self._process_timeouts()
|
||||
domain = self._get_domain(domain_name)
|
||||
wfe = domain.get_workflow_execution(
|
||||
workflow_id, run_id=run_id, raise_if_closed=True)
|
||||
workflow_id, run_id=run_id, raise_if_closed=True
|
||||
)
|
||||
wfe.signal(signal_name, input)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,15 @@ from .timeout import Timeout
|
|||
|
||||
|
||||
class ActivityTask(BaseModel):
|
||||
|
||||
def __init__(self, activity_id, activity_type, scheduled_event_id,
|
||||
workflow_execution, timeouts, input=None):
|
||||
def __init__(
|
||||
self,
|
||||
activity_id,
|
||||
activity_type,
|
||||
scheduled_event_id,
|
||||
workflow_execution,
|
||||
timeouts,
|
||||
input=None,
|
||||
):
|
||||
self.activity_id = activity_id
|
||||
self.activity_type = activity_type
|
||||
self.details = None
|
||||
|
|
@ -68,8 +74,9 @@ class ActivityTask(BaseModel):
|
|||
if not self.open or not self.workflow_execution.open:
|
||||
return None
|
||||
# TODO: handle the "NONE" case
|
||||
heartbeat_timeout_at = (self.last_heartbeat_timestamp +
|
||||
int(self.timeouts["heartbeatTimeout"]))
|
||||
heartbeat_timeout_at = self.last_heartbeat_timestamp + int(
|
||||
self.timeouts["heartbeatTimeout"]
|
||||
)
|
||||
_timeout = Timeout(self, heartbeat_timeout_at, "HEARTBEAT")
|
||||
if _timeout.reached:
|
||||
return _timeout
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from .generic_type import GenericType
|
|||
|
||||
|
||||
class ActivityType(GenericType):
|
||||
|
||||
@property
|
||||
def _configuration_keys(self):
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ from .timeout import Timeout
|
|||
|
||||
|
||||
class DecisionTask(BaseModel):
|
||||
|
||||
def __init__(self, workflow_execution, scheduled_event_id):
|
||||
self.workflow_execution = workflow_execution
|
||||
self.workflow_type = workflow_execution.workflow_type
|
||||
|
|
@ -19,7 +18,9 @@ class DecisionTask(BaseModel):
|
|||
self.previous_started_event_id = 0
|
||||
self.started_event_id = None
|
||||
self.started_timestamp = None
|
||||
self.start_to_close_timeout = self.workflow_execution.task_start_to_close_timeout
|
||||
self.start_to_close_timeout = (
|
||||
self.workflow_execution.task_start_to_close_timeout
|
||||
)
|
||||
self.state = "SCHEDULED"
|
||||
# this is *not* necessarily coherent with workflow execution history,
|
||||
# but that shouldn't be a problem for tests
|
||||
|
|
@ -37,9 +38,7 @@ class DecisionTask(BaseModel):
|
|||
def to_full_dict(self, reverse_order=False):
|
||||
events = self.workflow_execution.events(reverse_order=reverse_order)
|
||||
hsh = {
|
||||
"events": [
|
||||
evt.to_dict() for evt in events
|
||||
],
|
||||
"events": [evt.to_dict() for evt in events],
|
||||
"taskToken": self.task_token,
|
||||
"previousStartedEventId": self.previous_started_event_id,
|
||||
"workflowExecution": self.workflow_execution.to_short_dict(),
|
||||
|
|
@ -62,8 +61,7 @@ class DecisionTask(BaseModel):
|
|||
if not self.started or not self.workflow_execution.open:
|
||||
return None
|
||||
# TODO: handle the "NONE" case
|
||||
start_to_close_at = self.started_timestamp + \
|
||||
int(self.start_to_close_timeout)
|
||||
start_to_close_at = self.started_timestamp + int(self.start_to_close_timeout)
|
||||
_timeout = Timeout(self, start_to_close_at, "START_TO_CLOSE")
|
||||
if _timeout.reached:
|
||||
return _timeout
|
||||
|
|
|
|||
|
|
@ -9,16 +9,12 @@ from ..exceptions import (
|
|||
|
||||
|
||||
class Domain(BaseModel):
|
||||
|
||||
def __init__(self, name, retention, description=None):
|
||||
self.name = name
|
||||
self.retention = retention
|
||||
self.description = description
|
||||
self.status = "REGISTERED"
|
||||
self.types = {
|
||||
"activity": defaultdict(dict),
|
||||
"workflow": defaultdict(dict),
|
||||
}
|
||||
self.types = {"activity": defaultdict(dict), "workflow": defaultdict(dict)}
|
||||
# Workflow executions have an id, which unicity is guaranteed
|
||||
# at domain level (not super clear in the docs, but I checked
|
||||
# that against SWF API) ; hence the storage method as a dict
|
||||
|
|
@ -32,10 +28,7 @@ class Domain(BaseModel):
|
|||
return "Domain(name: %(name)s, status: %(status)s)" % self.__dict__
|
||||
|
||||
def to_short_dict(self):
|
||||
hsh = {
|
||||
"name": self.name,
|
||||
"status": self.status,
|
||||
}
|
||||
hsh = {"name": self.name, "status": self.status}
|
||||
if self.description:
|
||||
hsh["description"] = self.description
|
||||
return hsh
|
||||
|
|
@ -43,9 +36,7 @@ class Domain(BaseModel):
|
|||
def to_full_dict(self):
|
||||
return {
|
||||
"domainInfo": self.to_short_dict(),
|
||||
"configuration": {
|
||||
"workflowExecutionRetentionPeriodInDays": self.retention,
|
||||
}
|
||||
"configuration": {"workflowExecutionRetentionPeriodInDays": self.retention},
|
||||
}
|
||||
|
||||
def get_type(self, kind, name, version, ignore_empty=False):
|
||||
|
|
@ -57,7 +48,7 @@ class Domain(BaseModel):
|
|||
"type",
|
||||
"{0}Type=[name={1}, version={2}]".format(
|
||||
kind.capitalize(), name, version
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def add_type(self, _type):
|
||||
|
|
@ -77,15 +68,22 @@ class Domain(BaseModel):
|
|||
raise SWFWorkflowExecutionAlreadyStartedFault()
|
||||
self.workflow_executions.append(workflow_execution)
|
||||
|
||||
def get_workflow_execution(self, workflow_id, run_id=None,
|
||||
raise_if_none=True, raise_if_closed=False):
|
||||
def get_workflow_execution(
|
||||
self, workflow_id, run_id=None, raise_if_none=True, raise_if_closed=False
|
||||
):
|
||||
# query
|
||||
if run_id:
|
||||
_all = [w for w in self.workflow_executions
|
||||
if w.workflow_id == workflow_id and w.run_id == run_id]
|
||||
_all = [
|
||||
w
|
||||
for w in self.workflow_executions
|
||||
if w.workflow_id == workflow_id and w.run_id == run_id
|
||||
]
|
||||
else:
|
||||
_all = [w for w in self.workflow_executions
|
||||
if w.workflow_id == workflow_id and w.open]
|
||||
_all = [
|
||||
w
|
||||
for w in self.workflow_executions
|
||||
if w.workflow_id == workflow_id and w.open
|
||||
]
|
||||
# reduce
|
||||
wfe = _all[0] if _all else None
|
||||
# raise if closed / none
|
||||
|
|
@ -93,8 +91,12 @@ class Domain(BaseModel):
|
|||
wfe = None
|
||||
if not wfe and raise_if_none:
|
||||
if run_id:
|
||||
args = ["execution", "WorkflowExecution=[workflowId={0}, runId={1}]".format(
|
||||
workflow_id, run_id)]
|
||||
args = [
|
||||
"execution",
|
||||
"WorkflowExecution=[workflowId={0}, runId={1}]".format(
|
||||
workflow_id, run_id
|
||||
),
|
||||
]
|
||||
else:
|
||||
args = ["execution, workflowId = {0}".format(workflow_id)]
|
||||
raise SWFUnknownResourceFault(*args)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from moto.core.utils import camelcase_to_underscores
|
|||
|
||||
|
||||
class GenericType(BaseModel):
|
||||
|
||||
def __init__(self, name, version, **kwargs):
|
||||
self.name = name
|
||||
self.version = version
|
||||
|
|
@ -24,7 +23,9 @@ class GenericType(BaseModel):
|
|||
|
||||
def __repr__(self):
|
||||
cls = self.__class__.__name__
|
||||
attrs = "name: %(name)s, version: %(version)s, status: %(status)s" % self.__dict__
|
||||
attrs = (
|
||||
"name: %(name)s, version: %(version)s, status: %(status)s" % self.__dict__
|
||||
)
|
||||
return "{0}({1})".format(cls, attrs)
|
||||
|
||||
@property
|
||||
|
|
@ -36,10 +37,7 @@ class GenericType(BaseModel):
|
|||
raise NotImplementedError()
|
||||
|
||||
def to_short_dict(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"version": self.version,
|
||||
}
|
||||
return {"name": self.name, "version": self.version}
|
||||
|
||||
def to_medium_dict(self):
|
||||
hsh = {
|
||||
|
|
@ -54,10 +52,7 @@ class GenericType(BaseModel):
|
|||
return hsh
|
||||
|
||||
def to_full_dict(self):
|
||||
hsh = {
|
||||
"typeInfo": self.to_medium_dict(),
|
||||
"configuration": {}
|
||||
}
|
||||
hsh = {"typeInfo": self.to_medium_dict(), "configuration": {}}
|
||||
if self.task_list:
|
||||
hsh["configuration"]["defaultTaskList"] = {"name": self.task_list}
|
||||
for key in self._configuration_keys:
|
||||
|
|
|
|||
|
|
@ -25,17 +25,17 @@ SUPPORTED_HISTORY_EVENT_TYPES = (
|
|||
"ActivityTaskTimedOut",
|
||||
"DecisionTaskTimedOut",
|
||||
"WorkflowExecutionTimedOut",
|
||||
"WorkflowExecutionSignaled"
|
||||
"WorkflowExecutionSignaled",
|
||||
)
|
||||
|
||||
|
||||
class HistoryEvent(BaseModel):
|
||||
|
||||
def __init__(self, event_id, event_type, event_timestamp=None, **kwargs):
|
||||
if event_type not in SUPPORTED_HISTORY_EVENT_TYPES:
|
||||
raise NotImplementedError(
|
||||
"HistoryEvent does not implement attributes for type '{0}'".format(
|
||||
event_type)
|
||||
event_type
|
||||
)
|
||||
)
|
||||
self.event_id = event_id
|
||||
self.event_type = event_type
|
||||
|
|
@ -61,7 +61,7 @@ class HistoryEvent(BaseModel):
|
|||
"eventId": self.event_id,
|
||||
"eventType": self.event_type,
|
||||
"eventTimestamp": self.event_timestamp,
|
||||
self._attributes_key(): self.event_attributes
|
||||
self._attributes_key(): self.event_attributes,
|
||||
}
|
||||
|
||||
def _attributes_key(self):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from moto.core.utils import unix_time
|
|||
|
||||
|
||||
class Timeout(BaseModel):
|
||||
|
||||
def __init__(self, obj, timestamp, kind):
|
||||
self.obj = obj
|
||||
self.timestamp = timestamp
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import uuid
|
|||
from moto.core import BaseModel
|
||||
from moto.core.utils import camelcase_to_underscores, unix_time
|
||||
|
||||
from ..constants import (
|
||||
DECISIONS_FIELDS,
|
||||
)
|
||||
from ..constants import DECISIONS_FIELDS
|
||||
from ..exceptions import (
|
||||
SWFDefaultUndefinedFault,
|
||||
SWFValidationException,
|
||||
|
|
@ -38,7 +36,7 @@ class WorkflowExecution(BaseModel):
|
|||
"FailWorkflowExecution",
|
||||
"RequestCancelActivityTask",
|
||||
"StartChildWorkflowExecution",
|
||||
"CancelWorkflowExecution"
|
||||
"CancelWorkflowExecution",
|
||||
]
|
||||
|
||||
def __init__(self, domain, workflow_type, workflow_id, **kwargs):
|
||||
|
|
@ -66,11 +64,10 @@ class WorkflowExecution(BaseModel):
|
|||
# param is set, # SWF will raise DefaultUndefinedFault errors in the
|
||||
# same order as the few lines that follow)
|
||||
self._set_from_kwargs_or_workflow_type(
|
||||
kwargs, "execution_start_to_close_timeout")
|
||||
self._set_from_kwargs_or_workflow_type(
|
||||
kwargs, "task_list", "task_list")
|
||||
self._set_from_kwargs_or_workflow_type(
|
||||
kwargs, "task_start_to_close_timeout")
|
||||
kwargs, "execution_start_to_close_timeout"
|
||||
)
|
||||
self._set_from_kwargs_or_workflow_type(kwargs, "task_list", "task_list")
|
||||
self._set_from_kwargs_or_workflow_type(kwargs, "task_start_to_close_timeout")
|
||||
self._set_from_kwargs_or_workflow_type(kwargs, "child_policy")
|
||||
self.input = kwargs.get("input")
|
||||
# counters
|
||||
|
|
@ -89,7 +86,9 @@ class WorkflowExecution(BaseModel):
|
|||
def __repr__(self):
|
||||
return "WorkflowExecution(run_id: {0})".format(self.run_id)
|
||||
|
||||
def _set_from_kwargs_or_workflow_type(self, kwargs, local_key, workflow_type_key=None):
|
||||
def _set_from_kwargs_or_workflow_type(
|
||||
self, kwargs, local_key, workflow_type_key=None
|
||||
):
|
||||
if workflow_type_key is None:
|
||||
workflow_type_key = "default_" + local_key
|
||||
value = kwargs.get(local_key)
|
||||
|
|
@ -109,10 +108,7 @@ class WorkflowExecution(BaseModel):
|
|||
]
|
||||
|
||||
def to_short_dict(self):
|
||||
return {
|
||||
"workflowId": self.workflow_id,
|
||||
"runId": self.run_id
|
||||
}
|
||||
return {"workflowId": self.workflow_id, "runId": self.run_id}
|
||||
|
||||
def to_medium_dict(self):
|
||||
hsh = {
|
||||
|
|
@ -129,9 +125,7 @@ class WorkflowExecution(BaseModel):
|
|||
def to_full_dict(self):
|
||||
hsh = {
|
||||
"executionInfo": self.to_medium_dict(),
|
||||
"executionConfiguration": {
|
||||
"taskList": {"name": self.task_list}
|
||||
}
|
||||
"executionConfiguration": {"taskList": {"name": self.task_list}},
|
||||
}
|
||||
# configuration
|
||||
for key in self._configuration_keys:
|
||||
|
|
@ -152,23 +146,20 @@ class WorkflowExecution(BaseModel):
|
|||
|
||||
def to_list_dict(self):
|
||||
hsh = {
|
||||
'execution': {
|
||||
'workflowId': self.workflow_id,
|
||||
'runId': self.run_id,
|
||||
},
|
||||
'workflowType': self.workflow_type.to_short_dict(),
|
||||
'startTimestamp': self.start_timestamp,
|
||||
'executionStatus': self.execution_status,
|
||||
'cancelRequested': self.cancel_requested,
|
||||
"execution": {"workflowId": self.workflow_id, "runId": self.run_id},
|
||||
"workflowType": self.workflow_type.to_short_dict(),
|
||||
"startTimestamp": self.start_timestamp,
|
||||
"executionStatus": self.execution_status,
|
||||
"cancelRequested": self.cancel_requested,
|
||||
}
|
||||
if self.tag_list:
|
||||
hsh['tagList'] = self.tag_list
|
||||
hsh["tagList"] = self.tag_list
|
||||
if self.parent:
|
||||
hsh['parent'] = self.parent
|
||||
hsh["parent"] = self.parent
|
||||
if self.close_status:
|
||||
hsh['closeStatus'] = self.close_status
|
||||
hsh["closeStatus"] = self.close_status
|
||||
if self.close_timestamp:
|
||||
hsh['closeTimestamp'] = self.close_timestamp
|
||||
hsh["closeTimestamp"] = self.close_timestamp
|
||||
return hsh
|
||||
|
||||
def _process_timeouts(self):
|
||||
|
|
@ -206,10 +197,7 @@ class WorkflowExecution(BaseModel):
|
|||
# now find the first timeout to process
|
||||
first_timeout = None
|
||||
if timeout_candidates:
|
||||
first_timeout = min(
|
||||
timeout_candidates,
|
||||
key=lambda t: t.timestamp
|
||||
)
|
||||
first_timeout = min(timeout_candidates, key=lambda t: t.timestamp)
|
||||
|
||||
if first_timeout:
|
||||
should_schedule_decision_next = False
|
||||
|
|
@ -258,7 +246,7 @@ class WorkflowExecution(BaseModel):
|
|||
task_list=self.task_list,
|
||||
task_start_to_close_timeout=self.task_start_to_close_timeout,
|
||||
workflow_type=self.workflow_type,
|
||||
input=self.input
|
||||
input=self.input,
|
||||
)
|
||||
self.schedule_decision_task()
|
||||
|
||||
|
|
@ -269,8 +257,7 @@ class WorkflowExecution(BaseModel):
|
|||
task_list=self.task_list,
|
||||
)
|
||||
self.domain.add_to_decision_task_list(
|
||||
self.task_list,
|
||||
DecisionTask(self, evt.event_id),
|
||||
self.task_list, DecisionTask(self, evt.event_id)
|
||||
)
|
||||
self.open_counts["openDecisionTasks"] += 1
|
||||
|
||||
|
|
@ -285,32 +272,30 @@ class WorkflowExecution(BaseModel):
|
|||
|
||||
@property
|
||||
def decision_tasks(self):
|
||||
return [t for t in self.domain.decision_tasks
|
||||
if t.workflow_execution == self]
|
||||
return [t for t in self.domain.decision_tasks if t.workflow_execution == self]
|
||||
|
||||
@property
|
||||
def activity_tasks(self):
|
||||
return [t for t in self.domain.activity_tasks
|
||||
if t.workflow_execution == self]
|
||||
return [t for t in self.domain.activity_tasks if t.workflow_execution == self]
|
||||
|
||||
def _find_decision_task(self, task_token):
|
||||
for dt in self.decision_tasks:
|
||||
if dt.task_token == task_token:
|
||||
return dt
|
||||
raise ValueError(
|
||||
"No decision task with token: {0}".format(task_token)
|
||||
)
|
||||
raise ValueError("No decision task with token: {0}".format(task_token))
|
||||
|
||||
def start_decision_task(self, task_token, identity=None):
|
||||
dt = self._find_decision_task(task_token)
|
||||
evt = self._add_event(
|
||||
"DecisionTaskStarted",
|
||||
scheduled_event_id=dt.scheduled_event_id,
|
||||
identity=identity
|
||||
identity=identity,
|
||||
)
|
||||
dt.start(evt.event_id)
|
||||
|
||||
def complete_decision_task(self, task_token, decisions=None, execution_context=None):
|
||||
def complete_decision_task(
|
||||
self, task_token, decisions=None, execution_context=None
|
||||
):
|
||||
# 'decisions' can be None per boto.swf defaults, so replace it with something iterable
|
||||
if not decisions:
|
||||
decisions = []
|
||||
|
|
@ -336,12 +321,14 @@ class WorkflowExecution(BaseModel):
|
|||
constraints = DECISIONS_FIELDS.get(kind, {})
|
||||
for key, constraint in constraints.items():
|
||||
if constraint["required"] and not value.get(key):
|
||||
problems.append({
|
||||
"type": "null_value",
|
||||
"where": "decisions.{0}.member.{1}.{2}".format(
|
||||
decision_id, kind, key
|
||||
)
|
||||
})
|
||||
problems.append(
|
||||
{
|
||||
"type": "null_value",
|
||||
"where": "decisions.{0}.member.{1}.{2}".format(
|
||||
decision_id, kind, key
|
||||
),
|
||||
}
|
||||
)
|
||||
return problems
|
||||
|
||||
def validate_decisions(self, decisions):
|
||||
|
|
@ -362,9 +349,7 @@ class WorkflowExecution(BaseModel):
|
|||
"CancelWorkflowExecution",
|
||||
]
|
||||
if dcs["decisionType"] in close_decision_types:
|
||||
raise SWFValidationException(
|
||||
"Close must be last decision in list"
|
||||
)
|
||||
raise SWFValidationException("Close must be last decision in list")
|
||||
|
||||
decision_number = 0
|
||||
for dcs in decisions:
|
||||
|
|
@ -372,24 +357,29 @@ class WorkflowExecution(BaseModel):
|
|||
# check decision types mandatory attributes
|
||||
# NB: the real SWF service seems to check attributes even for attributes list
|
||||
# that are not in line with the decisionType, so we do the same
|
||||
attrs_to_check = [
|
||||
d for d in dcs.keys() if d.endswith("DecisionAttributes")]
|
||||
attrs_to_check = [d for d in dcs.keys() if d.endswith("DecisionAttributes")]
|
||||
if dcs["decisionType"] in self.KNOWN_DECISION_TYPES:
|
||||
decision_type = dcs["decisionType"]
|
||||
decision_attr = "{0}DecisionAttributes".format(
|
||||
decapitalize(decision_type))
|
||||
decapitalize(decision_type)
|
||||
)
|
||||
attrs_to_check.append(decision_attr)
|
||||
for attr in attrs_to_check:
|
||||
problems += self._check_decision_attributes(
|
||||
attr, dcs.get(attr, {}), decision_number)
|
||||
attr, dcs.get(attr, {}), decision_number
|
||||
)
|
||||
# check decision type is correct
|
||||
if dcs["decisionType"] not in self.KNOWN_DECISION_TYPES:
|
||||
problems.append({
|
||||
"type": "bad_decision_type",
|
||||
"value": dcs["decisionType"],
|
||||
"where": "decisions.{0}.member.decisionType".format(decision_number),
|
||||
"possible_values": ", ".join(self.KNOWN_DECISION_TYPES),
|
||||
})
|
||||
problems.append(
|
||||
{
|
||||
"type": "bad_decision_type",
|
||||
"value": dcs["decisionType"],
|
||||
"where": "decisions.{0}.member.decisionType".format(
|
||||
decision_number
|
||||
),
|
||||
"possible_values": ", ".join(self.KNOWN_DECISION_TYPES),
|
||||
}
|
||||
)
|
||||
|
||||
# raise if any problem
|
||||
if any(problems):
|
||||
|
|
@ -403,14 +393,12 @@ class WorkflowExecution(BaseModel):
|
|||
# handle each decision separately, in order
|
||||
for decision in decisions:
|
||||
decision_type = decision["decisionType"]
|
||||
attributes_key = "{0}DecisionAttributes".format(
|
||||
decapitalize(decision_type))
|
||||
attributes_key = "{0}DecisionAttributes".format(decapitalize(decision_type))
|
||||
attributes = decision.get(attributes_key, {})
|
||||
if decision_type == "CompleteWorkflowExecution":
|
||||
self.complete(event_id, attributes.get("result"))
|
||||
elif decision_type == "FailWorkflowExecution":
|
||||
self.fail(event_id, attributes.get(
|
||||
"details"), attributes.get("reason"))
|
||||
self.fail(event_id, attributes.get("details"), attributes.get("reason"))
|
||||
elif decision_type == "ScheduleActivityTask":
|
||||
self.schedule_activity_task(event_id, attributes)
|
||||
else:
|
||||
|
|
@ -425,7 +413,8 @@ class WorkflowExecution(BaseModel):
|
|||
# TODO: implement Decision type: StartChildWorkflowExecution
|
||||
# TODO: implement Decision type: StartTimer
|
||||
raise NotImplementedError(
|
||||
"Cannot handle decision: {0}".format(decision_type))
|
||||
"Cannot handle decision: {0}".format(decision_type)
|
||||
)
|
||||
|
||||
# finally decrement counter if and only if everything went well
|
||||
self.open_counts["openDecisionTasks"] -= 1
|
||||
|
|
@ -475,18 +464,21 @@ class WorkflowExecution(BaseModel):
|
|||
ignore_empty=True,
|
||||
)
|
||||
if not activity_type:
|
||||
fake_type = ActivityType(attributes["activityType"]["name"],
|
||||
attributes["activityType"]["version"])
|
||||
fail_schedule_activity_task(fake_type,
|
||||
"ACTIVITY_TYPE_DOES_NOT_EXIST")
|
||||
fake_type = ActivityType(
|
||||
attributes["activityType"]["name"],
|
||||
attributes["activityType"]["version"],
|
||||
)
|
||||
fail_schedule_activity_task(fake_type, "ACTIVITY_TYPE_DOES_NOT_EXIST")
|
||||
return
|
||||
if activity_type.status == "DEPRECATED":
|
||||
fail_schedule_activity_task(activity_type,
|
||||
"ACTIVITY_TYPE_DEPRECATED")
|
||||
fail_schedule_activity_task(activity_type, "ACTIVITY_TYPE_DEPRECATED")
|
||||
return
|
||||
if any(at for at in self.activity_tasks if at.activity_id == attributes["activityId"]):
|
||||
fail_schedule_activity_task(activity_type,
|
||||
"ACTIVITY_ID_ALREADY_IN_USE")
|
||||
if any(
|
||||
at
|
||||
for at in self.activity_tasks
|
||||
if at.activity_id == attributes["activityId"]
|
||||
):
|
||||
fail_schedule_activity_task(activity_type, "ACTIVITY_ID_ALREADY_IN_USE")
|
||||
return
|
||||
|
||||
# find task list or default task list, else fail
|
||||
|
|
@ -494,20 +486,25 @@ class WorkflowExecution(BaseModel):
|
|||
if not task_list and activity_type.task_list:
|
||||
task_list = activity_type.task_list
|
||||
if not task_list:
|
||||
fail_schedule_activity_task(activity_type,
|
||||
"DEFAULT_TASK_LIST_UNDEFINED")
|
||||
fail_schedule_activity_task(activity_type, "DEFAULT_TASK_LIST_UNDEFINED")
|
||||
return
|
||||
|
||||
# find timeouts or default timeout, else fail
|
||||
timeouts = {}
|
||||
for _type in ["scheduleToStartTimeout", "scheduleToCloseTimeout", "startToCloseTimeout", "heartbeatTimeout"]:
|
||||
for _type in [
|
||||
"scheduleToStartTimeout",
|
||||
"scheduleToCloseTimeout",
|
||||
"startToCloseTimeout",
|
||||
"heartbeatTimeout",
|
||||
]:
|
||||
default_key = "default_task_" + camelcase_to_underscores(_type)
|
||||
default_value = getattr(activity_type, default_key)
|
||||
timeouts[_type] = attributes.get(_type, default_value)
|
||||
if not timeouts[_type]:
|
||||
error_key = default_key.replace("default_task_", "default_")
|
||||
fail_schedule_activity_task(activity_type,
|
||||
"{0}_UNDEFINED".format(error_key.upper()))
|
||||
fail_schedule_activity_task(
|
||||
activity_type, "{0}_UNDEFINED".format(error_key.upper())
|
||||
)
|
||||
return
|
||||
|
||||
# Only add event and increment counters now that nothing went wrong
|
||||
|
|
@ -541,16 +538,14 @@ class WorkflowExecution(BaseModel):
|
|||
for task in self.activity_tasks:
|
||||
if task.task_token == task_token:
|
||||
return task
|
||||
raise ValueError(
|
||||
"No activity task with token: {0}".format(task_token)
|
||||
)
|
||||
raise ValueError("No activity task with token: {0}".format(task_token))
|
||||
|
||||
def start_activity_task(self, task_token, identity=None):
|
||||
task = self._find_activity_task(task_token)
|
||||
evt = self._add_event(
|
||||
"ActivityTaskStarted",
|
||||
scheduled_event_id=task.scheduled_event_id,
|
||||
identity=identity
|
||||
identity=identity,
|
||||
)
|
||||
task.start(evt.event_id)
|
||||
|
||||
|
|
@ -601,17 +596,16 @@ class WorkflowExecution(BaseModel):
|
|||
|
||||
def signal(self, signal_name, input):
|
||||
self._add_event(
|
||||
"WorkflowExecutionSignaled",
|
||||
signal_name=signal_name,
|
||||
input=input,
|
||||
"WorkflowExecutionSignaled", signal_name=signal_name, input=input
|
||||
)
|
||||
self.schedule_decision_task()
|
||||
|
||||
def first_timeout(self):
|
||||
if not self.open or not self.start_timestamp:
|
||||
return None
|
||||
start_to_close_at = self.start_timestamp + \
|
||||
int(self.execution_start_to_close_timeout)
|
||||
start_to_close_at = self.start_timestamp + int(
|
||||
self.execution_start_to_close_timeout
|
||||
)
|
||||
_timeout = Timeout(self, start_to_close_at, "START_TO_CLOSE")
|
||||
if _timeout.reached:
|
||||
return _timeout
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from .generic_type import GenericType
|
|||
|
||||
|
||||
class WorkflowType(GenericType):
|
||||
|
||||
@property
|
||||
def _configuration_keys(self):
|
||||
return [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue