Add first version of SWF endpoint RespondDecisionTaskCompleted

There's just the structure for now, for now the workflow execution
doesn't know how to handle any decision type.
This commit is contained in:
Jean-Baptiste Barth 2015-10-12 11:08:52 +02:00
commit d97c770849
8 changed files with 211 additions and 2 deletions

View file

@ -11,6 +11,7 @@ from ..exceptions import (
SWFSerializationException,
SWFTypeAlreadyExistsFault,
SWFTypeDeprecatedFault,
SWFValidationException,
)
from .activity_type import ActivityType
from .decision_task import DecisionTask
@ -201,6 +202,59 @@ class SWFBackend(BaseBackend):
count += wfe.open_counts["openDecisionTasks"]
return count
def respond_decision_task_completed(self, task_token,
decisions=None,
execution_context=None):
self._check_string(task_token)
self._check_none_or_string(execution_context)
# let's find decision task
decision_task = None
for domain in self.domains:
for _, wfe in domain.workflow_executions.iteritems():
for dt in wfe.decision_tasks:
if dt.task_token == task_token:
decision_task = dt
# no decision task found
if not decision_task:
# In the real world, SWF distinguishes an obviously invalid token and a
# token that has no corresponding decision task. For the latter it seems
# to wait until a task with that token comes up (which looks like a smart
# choice in an eventually-consistent system). The call doesn't seem to
# timeout shortly, it takes 3 or 4 minutes to result in:
# BotoServerError: 500 Internal Server Error
# {"__type":"com.amazon.coral.service#InternalFailure"}
# This behavior is not documented clearly in SWF docs and we'll ignore it
# in moto, as there is no obvious reason to rely on it in tests.
raise SWFValidationException("Invalid token")
# decision task found, but WorflowExecution is CLOSED
wfe = decision_task.workflow_execution
if wfe.execution_status != "OPEN":
raise SWFUnknownResourceFault(
"execution",
"WorkflowExecution=[workflowId={}, runId={}]".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 = {}".format(decision_task.scheduled_event_id)
)
else:
raise ValueError(
"This shouldn't happen: you have to PollForDecisionTask to get a token, "
"which changes DecisionTask status to 'STARTED' ; then it can only change "
"to 'COMPLETED'. If you didn't hack moto/swf internals, this is probably "
"a bug in moto, please report it, thanks!"
)
# 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)
swf_backends = {}
for region in boto.swf.regions():

View file

@ -34,3 +34,6 @@ class DecisionTask(object):
def start(self, started_event_id):
self.state = "STARTED"
self.started_event_id = started_event_id
def complete(self):
self.state = "COMPLETED"

View file

@ -56,6 +56,14 @@ class HistoryEvent(object):
if hasattr(self, "identity") and self.identity:
hsh["identity"] = self.identity
return hsh
elif self.event_type == "DecisionTaskCompleted":
hsh = {
"scheduledEventId": self.scheduled_event_id,
"startedEventId": self.started_event_id,
}
if hasattr(self, "execution_context") and self.execution_context:
hsh["executionContext"] = self.execution_context
return hsh
else:
raise NotImplementedError(
"HistoryEvent does not implement attributes for type '{}'".format(self.event_type)

View file

@ -151,3 +151,42 @@ class WorkflowExecution(object):
identity=identity
)
dt.start(evt.event_id)
def complete_decision_task(self, task_token, decisions=None, execution_context=None):
# TODO: check if decision can really complete in case of malformed "decisions"
dt = self._find_decision_task(task_token)
evt = self._add_event(
"DecisionTaskCompleted",
scheduled_event_id=dt.scheduled_event_id,
started_event_id=dt.started_event_id,
execution_context=execution_context,
)
dt.complete()
self.handle_decisions(decisions)
def handle_decisions(self, decisions):
"""
Handles a Decision according to SWF docs.
See: http://docs.aws.amazon.com/amazonswf/latest/apireference/API_Decision.html
"""
# 'decisions' can be None per boto.swf defaults, so better exiting
# directly for falsy values
if not decisions:
return
# handle each decision separately, in order
for decision in decisions:
decision_type = decision["decisionType"]
# TODO: implement Decision type: CancelTimer
# TODO: implement Decision type: CancelWorkflowExecution
# TODO: implement Decision type: CompleteWorkflowExecution
# TODO: implement Decision type: ContinueAsNewWorkflowExecution
# TODO: implement Decision type: FailWorkflowExecution
# TODO: implement Decision type: RecordMarker
# TODO: implement Decision type: RequestCancelActivityTask
# TODO: implement Decision type: RequestCancelExternalWorkflowExecution
# TODO: implement Decision type: ScheduleActivityTask
# TODO: implement Decision type: ScheduleLambdaFunction
# TODO: implement Decision type: SignalExternalWorkflowExecution
# TODO: implement Decision type: StartChildWorkflowExecution
# TODO: implement Decision type: StartTimer
raise NotImplementedError("Cannot handle decision: {}".format(decision_type))