Implement ScheduleActivityTask decision
This commit is contained in:
parent
53630dc061
commit
5e086223c2
6 changed files with 371 additions and 5 deletions
|
|
@ -23,6 +23,7 @@ class Domain(object):
|
|||
# of "workflow_id (client determined)" => WorkflowExecution()
|
||||
# here.
|
||||
self.workflow_executions = {}
|
||||
self.task_lists = defaultdict(list)
|
||||
|
||||
def __repr__(self):
|
||||
return "Domain(name: %(name)s, status: %(status)s)" % self.__dict__
|
||||
|
|
@ -83,3 +84,6 @@ class Domain(object):
|
|||
)
|
||||
)
|
||||
return wfe
|
||||
|
||||
def add_to_task_list(self, task_list, obj):
|
||||
self.task_lists[task_list].append(obj)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@ class GenericType(object):
|
|||
self.description = kwargs.pop("description")
|
||||
for key, value in kwargs.iteritems():
|
||||
self.__setattr__(key, value)
|
||||
# default values set to none
|
||||
for key in self._configuration_keys:
|
||||
attr = camelcase_to_underscores(key)
|
||||
if not hasattr(self, attr):
|
||||
self.__setattr__(attr, None)
|
||||
if not hasattr(self, "task_list"):
|
||||
self.task_list = None
|
||||
|
||||
def __repr__(self):
|
||||
cls = self.__class__.__name__
|
||||
|
|
@ -49,12 +56,10 @@ class GenericType(object):
|
|||
"typeInfo": self.to_medium_dict(),
|
||||
"configuration": {}
|
||||
}
|
||||
if hasattr(self, "task_list"):
|
||||
if self.task_list:
|
||||
hsh["configuration"]["defaultTaskList"] = {"name": self.task_list}
|
||||
for key in self._configuration_keys:
|
||||
attr = camelcase_to_underscores(key)
|
||||
if not hasattr(self, attr):
|
||||
continue
|
||||
if not getattr(self, attr):
|
||||
continue
|
||||
hsh["configuration"][key] = getattr(self, attr)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,30 @@ class HistoryEvent(object):
|
|||
if hasattr(self, "reason") and self.reason:
|
||||
hsh["reason"] = self.reason
|
||||
return hsh
|
||||
elif self.event_type == "ActivityTaskScheduled":
|
||||
hsh = {
|
||||
"activityId": self.attributes["activityId"],
|
||||
"activityType": self.activity_type.to_short_dict(),
|
||||
"decisionTaskCompletedEventId": self.decision_task_completed_event_id,
|
||||
"taskList": {
|
||||
"name": self.task_list,
|
||||
},
|
||||
}
|
||||
for attr in ["control", "heartbeatTimeout", "input", "scheduleToCloseTimeout",
|
||||
"scheduleToStartTimeout", "startToCloseTimeout", "taskPriority"]:
|
||||
if self.attributes.get(attr):
|
||||
hsh[attr] = self.attributes[attr]
|
||||
return hsh
|
||||
elif self.event_type == "ScheduleActivityTaskFailed":
|
||||
# TODO: implement other possible failure mode: OPEN_ACTIVITIES_LIMIT_EXCEEDED
|
||||
# NB: some failure modes are not implemented and probably won't be implemented in the
|
||||
# future, such as ACTIVITY_CREATION_RATE_EXCEEDED or OPERATION_NOT_PERMITTED
|
||||
return {
|
||||
"activityId": self.activity_id,
|
||||
"activityType": self.activity_type.to_short_dict(),
|
||||
"cause": self.cause,
|
||||
"decisionTaskCompletedEventId": self.decision_task_completed_event_id,
|
||||
}
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"HistoryEvent does not implement attributes for type '{}'".format(self.event_type)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ from ..exceptions import (
|
|||
SWFDecisionValidationException,
|
||||
)
|
||||
from ..utils import decapitalize
|
||||
from .activity_task import ActivityTask
|
||||
from .activity_type import ActivityType
|
||||
from .decision_task import DecisionTask
|
||||
from .history_event import HistoryEvent
|
||||
|
||||
|
|
@ -209,7 +211,10 @@ class WorkflowExecution(object):
|
|||
execution_context=execution_context,
|
||||
)
|
||||
dt.complete()
|
||||
self.should_schedule_decision_next = False
|
||||
self.handle_decisions(evt.event_id, decisions)
|
||||
if self.should_schedule_decision_next:
|
||||
self.schedule_decision_task()
|
||||
|
||||
def _check_decision_attributes(self, kind, value, decision_id):
|
||||
problems = []
|
||||
|
|
@ -293,6 +298,8 @@ class WorkflowExecution(object):
|
|||
self.complete(event_id, attributes.get("result"))
|
||||
elif decision_type == "FailWorkflowExecution":
|
||||
self.fail(event_id, attributes.get("details"), attributes.get("reason"))
|
||||
elif decision_type == "ScheduleActivityTask":
|
||||
self.schedule_activity_task(event_id, attributes)
|
||||
else:
|
||||
# TODO: implement Decision type: CancelTimer
|
||||
# TODO: implement Decision type: CancelWorkflowExecution
|
||||
|
|
@ -300,7 +307,6 @@ class WorkflowExecution(object):
|
|||
# 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
|
||||
|
|
@ -331,3 +337,96 @@ class WorkflowExecution(object):
|
|||
details=details,
|
||||
reason=reason,
|
||||
)
|
||||
|
||||
def schedule_activity_task(self, event_id, attributes):
|
||||
activity_type = self.domain.get_type(
|
||||
"activity",
|
||||
attributes["activityType"]["name"],
|
||||
attributes["activityType"]["version"],
|
||||
ignore_empty=True,
|
||||
)
|
||||
if not activity_type:
|
||||
fake_type = ActivityType(attributes["activityType"]["name"],
|
||||
attributes["activityType"]["version"])
|
||||
self._add_event(
|
||||
"ScheduleActivityTaskFailed",
|
||||
activity_id=attributes["activityId"],
|
||||
activity_type=fake_type,
|
||||
cause="ACTIVITY_TYPE_DOES_NOT_EXIST",
|
||||
decision_task_completed_event_id=event_id,
|
||||
)
|
||||
self.should_schedule_decision_next = True
|
||||
return
|
||||
if activity_type.status == "DEPRECATED":
|
||||
self._add_event(
|
||||
"ScheduleActivityTaskFailed",
|
||||
activity_id=attributes["activityId"],
|
||||
activity_type=activity_type,
|
||||
cause="ACTIVITY_TYPE_DEPRECATED",
|
||||
decision_task_completed_event_id=event_id,
|
||||
)
|
||||
self.should_schedule_decision_next = True
|
||||
return
|
||||
if any(at for at in self.activity_tasks
|
||||
if at.activity_id == attributes["activityId"]):
|
||||
self._add_event(
|
||||
"ScheduleActivityTaskFailed",
|
||||
activity_id=attributes["activityId"],
|
||||
activity_type=activity_type,
|
||||
cause="ACTIVITY_ID_ALREADY_IN_USE",
|
||||
decision_task_completed_event_id=event_id,
|
||||
)
|
||||
self.should_schedule_decision_next = True
|
||||
return
|
||||
|
||||
# find task list or default task list, else fail
|
||||
task_list = attributes.get("taskList", {}).get("name")
|
||||
if not task_list and activity_type.task_list:
|
||||
task_list = activity_type.task_list
|
||||
if not task_list:
|
||||
self._add_event(
|
||||
"ScheduleActivityTaskFailed",
|
||||
activity_id=attributes["activityId"],
|
||||
activity_type=activity_type,
|
||||
cause="DEFAULT_TASK_LIST_UNDEFINED",
|
||||
decision_task_completed_event_id=event_id,
|
||||
)
|
||||
self.should_schedule_decision_next = True
|
||||
return
|
||||
|
||||
# find timeouts or default timeout, else fail
|
||||
timeouts = {}
|
||||
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_")
|
||||
self._add_event(
|
||||
"ScheduleActivityTaskFailed",
|
||||
activity_id=attributes["activityId"],
|
||||
activity_type=activity_type,
|
||||
cause="{}_UNDEFINED".format(error_key.upper()),
|
||||
decision_task_completed_event_id=event_id,
|
||||
)
|
||||
self.should_schedule_decision_next = True
|
||||
return
|
||||
|
||||
task = ActivityTask(
|
||||
activity_id=attributes["activityId"],
|
||||
activity_type=activity_type,
|
||||
input=attributes.get("input"),
|
||||
workflow_execution=self,
|
||||
)
|
||||
# Only add event and increment counters if nothing went wrong
|
||||
# TODO: don't store activity tasks in 2 places...
|
||||
self.activity_tasks.append(task)
|
||||
self.domain.add_to_task_list(task_list, task)
|
||||
self._add_event(
|
||||
"ActivityTaskScheduled",
|
||||
decision_task_completed_event_id=event_id,
|
||||
activity_type=activity_type,
|
||||
attributes=attributes,
|
||||
task_list=task_list,
|
||||
)
|
||||
self.open_counts["openActivityTasks"] += 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue