Split SWF models into their own file
Given the docs[1] we will implement a hundred models or so if we want to have a full implementation of the SWF API, so better not have a 3k lines long models.py file, too hard to manipulate. [1] http://docs.aws.amazon.com/amazonswf/latest/apireference/API_DecisionTask.html
This commit is contained in:
parent
2878252816
commit
1026fb819f
@ -1,398 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from collections import defaultdict
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import boto.swf
|
|
||||||
|
|
||||||
from moto.core import BaseBackend
|
|
||||||
from moto.core.utils import camelcase_to_underscores
|
|
||||||
|
|
||||||
from .exceptions import (
|
|
||||||
SWFUnknownResourceFault,
|
|
||||||
SWFDomainAlreadyExistsFault,
|
|
||||||
SWFDomainDeprecatedFault,
|
|
||||||
SWFSerializationException,
|
|
||||||
SWFTypeAlreadyExistsFault,
|
|
||||||
SWFTypeDeprecatedFault,
|
|
||||||
SWFWorkflowExecutionAlreadyStartedFault,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Domain(object):
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
# 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
|
|
||||||
# of "workflow_id (client determined)" => WorkflowExecution()
|
|
||||||
# here.
|
|
||||||
self.workflow_executions = {}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "Domain(name: %(name)s, status: %(status)s)" % self.__dict__
|
|
||||||
|
|
||||||
def to_short_dict(self):
|
|
||||||
hsh = {
|
|
||||||
"name": self.name,
|
|
||||||
"status": self.status,
|
|
||||||
}
|
|
||||||
if self.description:
|
|
||||||
hsh["description"] = self.description
|
|
||||||
return hsh
|
|
||||||
|
|
||||||
def to_full_dict(self):
|
|
||||||
return {
|
|
||||||
"domainInfo": self.to_short_dict(),
|
|
||||||
"configuration": {
|
|
||||||
"workflowExecutionRetentionPeriodInDays": self.retention,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_type(self, kind, name, version, ignore_empty=False):
|
|
||||||
try:
|
|
||||||
return self.types[kind][name][version]
|
|
||||||
except KeyError:
|
|
||||||
if not ignore_empty:
|
|
||||||
raise SWFUnknownResourceFault(
|
|
||||||
"type",
|
|
||||||
"{}Type=[name={}, version={}]".format(
|
|
||||||
kind.capitalize(), name, version
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_type(self, _type):
|
|
||||||
self.types[_type.kind][_type.name][_type.version] = _type
|
|
||||||
|
|
||||||
def find_types(self, kind, status):
|
|
||||||
_all = []
|
|
||||||
for _, family in self.types[kind].iteritems():
|
|
||||||
for _, _type in family.iteritems():
|
|
||||||
if _type.status == status:
|
|
||||||
_all.append(_type)
|
|
||||||
return _all
|
|
||||||
|
|
||||||
def add_workflow_execution(self, workflow_execution):
|
|
||||||
_id = workflow_execution.workflow_id
|
|
||||||
if self.workflow_executions.get(_id):
|
|
||||||
raise SWFWorkflowExecutionAlreadyStartedFault()
|
|
||||||
self.workflow_executions[_id] = workflow_execution
|
|
||||||
|
|
||||||
def get_workflow_execution(self, run_id, workflow_id):
|
|
||||||
wfe = self.workflow_executions.get(workflow_id)
|
|
||||||
if not wfe or wfe.run_id != run_id:
|
|
||||||
raise SWFUnknownResourceFault(
|
|
||||||
"execution",
|
|
||||||
"WorkflowExecution=[workflowId={}, runId={}]".format(
|
|
||||||
workflow_id, run_id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return wfe
|
|
||||||
|
|
||||||
|
|
||||||
class GenericType(object):
|
|
||||||
def __init__(self, name, version, **kwargs):
|
|
||||||
self.name = name
|
|
||||||
self.version = version
|
|
||||||
self.status = "REGISTERED"
|
|
||||||
if "description" in kwargs:
|
|
||||||
self.description = kwargs.pop("description")
|
|
||||||
for key, value in kwargs.iteritems():
|
|
||||||
self.__setattr__(key, value)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
cls = self.__class__.__name__
|
|
||||||
attrs = "name: %(name)s, version: %(version)s, status: %(status)s" % self.__dict__
|
|
||||||
return "{}({})".format(cls, attrs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def kind(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _configuration_keys(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def to_short_dict(self):
|
|
||||||
return {
|
|
||||||
"name": self.name,
|
|
||||||
"version": self.version,
|
|
||||||
}
|
|
||||||
|
|
||||||
def to_medium_dict(self):
|
|
||||||
hsh = {
|
|
||||||
"{}Type".format(self.kind): self.to_short_dict(),
|
|
||||||
"creationDate": 1420066800,
|
|
||||||
"status": self.status,
|
|
||||||
}
|
|
||||||
if self.status == "DEPRECATED":
|
|
||||||
hsh["deprecationDate"] = 1422745200
|
|
||||||
if hasattr(self, "description"):
|
|
||||||
hsh["description"] = self.description
|
|
||||||
return hsh
|
|
||||||
|
|
||||||
def to_full_dict(self):
|
|
||||||
hsh = {
|
|
||||||
"typeInfo": self.to_medium_dict(),
|
|
||||||
"configuration": {}
|
|
||||||
}
|
|
||||||
if hasattr(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)
|
|
||||||
return hsh
|
|
||||||
|
|
||||||
|
|
||||||
class ActivityType(GenericType):
|
|
||||||
@property
|
|
||||||
def _configuration_keys(self):
|
|
||||||
return [
|
|
||||||
"defaultTaskHeartbeatTimeout",
|
|
||||||
"defaultTaskScheduleToCloseTimeout",
|
|
||||||
"defaultTaskScheduleToStartTimeout",
|
|
||||||
"defaultTaskStartToCloseTimeout",
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def kind(self):
|
|
||||||
return "activity"
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowType(GenericType):
|
|
||||||
@property
|
|
||||||
def _configuration_keys(self):
|
|
||||||
return [
|
|
||||||
"defaultChildPolicy",
|
|
||||||
"defaultExecutionStartToCloseTimeout",
|
|
||||||
"defaultTaskStartToCloseTimeout",
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def kind(self):
|
|
||||||
return "workflow"
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowExecution(object):
|
|
||||||
def __init__(self, workflow_type, workflow_id, **kwargs):
|
|
||||||
self.workflow_type = workflow_type
|
|
||||||
self.workflow_id = workflow_id
|
|
||||||
self.run_id = uuid.uuid4().hex
|
|
||||||
self.execution_status = "OPEN"
|
|
||||||
self.cancel_requested = False
|
|
||||||
#config
|
|
||||||
for key, value in kwargs.iteritems():
|
|
||||||
self.__setattr__(key, value)
|
|
||||||
#counters
|
|
||||||
self.open_counts = {
|
|
||||||
"openTimers": 0,
|
|
||||||
"openDecisionTasks": 0,
|
|
||||||
"openActivityTasks": 0,
|
|
||||||
"openChildWorkflowExecutions": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "WorkflowExecution(run_id: {})".format(self.run_id)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _configuration_keys(self):
|
|
||||||
return [
|
|
||||||
"executionStartToCloseTimeout",
|
|
||||||
"childPolicy",
|
|
||||||
"taskPriority",
|
|
||||||
"taskStartToCloseTimeout",
|
|
||||||
]
|
|
||||||
|
|
||||||
def to_short_dict(self):
|
|
||||||
return {
|
|
||||||
"workflowId": self.workflow_id,
|
|
||||||
"runId": self.run_id
|
|
||||||
}
|
|
||||||
|
|
||||||
def to_medium_dict(self):
|
|
||||||
hsh = {
|
|
||||||
"execution": self.to_short_dict(),
|
|
||||||
"workflowType": self.workflow_type.to_short_dict(),
|
|
||||||
"startTimestamp": 1420066800.123,
|
|
||||||
"executionStatus": self.execution_status,
|
|
||||||
"cancelRequested": self.cancel_requested,
|
|
||||||
}
|
|
||||||
if hasattr(self, "tag_list"):
|
|
||||||
hsh["tagList"] = self.tag_list
|
|
||||||
return hsh
|
|
||||||
|
|
||||||
def to_full_dict(self):
|
|
||||||
hsh = {
|
|
||||||
"executionInfo": self.to_medium_dict(),
|
|
||||||
"executionConfiguration": {}
|
|
||||||
}
|
|
||||||
#configuration
|
|
||||||
if hasattr(self, "task_list"):
|
|
||||||
hsh["executionConfiguration"]["taskList"] = {"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["executionConfiguration"][key] = getattr(self, attr)
|
|
||||||
#counters
|
|
||||||
hsh["openCounts"] = self.open_counts
|
|
||||||
return hsh
|
|
||||||
|
|
||||||
|
|
||||||
class SWFBackend(BaseBackend):
|
|
||||||
def __init__(self, region_name):
|
|
||||||
self.region_name = region_name
|
|
||||||
self.domains = []
|
|
||||||
super(SWFBackend, self).__init__()
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
region_name = self.region_name
|
|
||||||
self.__dict__ = {}
|
|
||||||
self.__init__(region_name)
|
|
||||||
|
|
||||||
def _get_domain(self, name, ignore_empty=False):
|
|
||||||
matching = [domain for domain in self.domains if domain.name == name]
|
|
||||||
if not matching and not ignore_empty:
|
|
||||||
raise SWFUnknownResourceFault("domain", name)
|
|
||||||
if matching:
|
|
||||||
return matching[0]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _check_none_or_string(self, parameter):
|
|
||||||
if parameter is not None:
|
|
||||||
self._check_string(parameter)
|
|
||||||
|
|
||||||
def _check_string(self, parameter):
|
|
||||||
if not isinstance(parameter, basestring):
|
|
||||||
raise SWFSerializationException(parameter)
|
|
||||||
|
|
||||||
def _check_none_or_list_of_strings(self, parameter):
|
|
||||||
if parameter is not None:
|
|
||||||
self._check_list_of_strings(parameter)
|
|
||||||
|
|
||||||
def _check_list_of_strings(self, parameter):
|
|
||||||
if not isinstance(parameter, list):
|
|
||||||
raise SWFSerializationException(parameter)
|
|
||||||
for i in parameter:
|
|
||||||
if not isinstance(i, basestring):
|
|
||||||
raise SWFSerializationException(parameter)
|
|
||||||
|
|
||||||
def list_domains(self, status, reverse_order=None):
|
|
||||||
self._check_string(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 register_domain(self, name, workflow_execution_retention_period_in_days,
|
|
||||||
description=None):
|
|
||||||
self._check_string(name)
|
|
||||||
self._check_string(workflow_execution_retention_period_in_days)
|
|
||||||
self._check_none_or_string(description)
|
|
||||||
if self._get_domain(name, ignore_empty=True):
|
|
||||||
raise SWFDomainAlreadyExistsFault(name)
|
|
||||||
domain = Domain(name, workflow_execution_retention_period_in_days,
|
|
||||||
description)
|
|
||||||
self.domains.append(domain)
|
|
||||||
|
|
||||||
def deprecate_domain(self, name):
|
|
||||||
self._check_string(name)
|
|
||||||
domain = self._get_domain(name)
|
|
||||||
if domain.status == "DEPRECATED":
|
|
||||||
raise SWFDomainDeprecatedFault(name)
|
|
||||||
domain.status = "DEPRECATED"
|
|
||||||
|
|
||||||
def describe_domain(self, name):
|
|
||||||
self._check_string(name)
|
|
||||||
return self._get_domain(name)
|
|
||||||
|
|
||||||
def list_types(self, kind, domain_name, status, reverse_order=None):
|
|
||||||
self._check_string(domain_name)
|
|
||||||
self._check_string(status)
|
|
||||||
domain = self._get_domain(domain_name)
|
|
||||||
_types = domain.find_types(kind, status)
|
|
||||||
_types = sorted(_types, key=lambda domain: domain.name)
|
|
||||||
if reverse_order:
|
|
||||||
_types = reversed(_types)
|
|
||||||
return _types
|
|
||||||
|
|
||||||
def register_type(self, kind, domain_name, name, version, **kwargs):
|
|
||||||
self._check_string(domain_name)
|
|
||||||
self._check_string(name)
|
|
||||||
self._check_string(version)
|
|
||||||
for _, value in kwargs.iteritems():
|
|
||||||
self._check_none_or_string(value)
|
|
||||||
domain = self._get_domain(domain_name)
|
|
||||||
_type = domain.get_type(kind, name, version, ignore_empty=True)
|
|
||||||
if _type:
|
|
||||||
raise SWFTypeAlreadyExistsFault(_type)
|
|
||||||
_class = globals()["{}Type".format(kind.capitalize())]
|
|
||||||
_type = _class(name, version, **kwargs)
|
|
||||||
domain.add_type(_type)
|
|
||||||
|
|
||||||
def deprecate_type(self, kind, domain_name, name, version):
|
|
||||||
self._check_string(domain_name)
|
|
||||||
self._check_string(name)
|
|
||||||
self._check_string(version)
|
|
||||||
domain = self._get_domain(domain_name)
|
|
||||||
_type = domain.get_type(kind, name, version)
|
|
||||||
if _type.status == "DEPRECATED":
|
|
||||||
raise SWFTypeDeprecatedFault(_type)
|
|
||||||
_type.status = "DEPRECATED"
|
|
||||||
|
|
||||||
def describe_type(self, kind, domain_name, name, version):
|
|
||||||
self._check_string(domain_name)
|
|
||||||
self._check_string(name)
|
|
||||||
self._check_string(version)
|
|
||||||
domain = self._get_domain(domain_name)
|
|
||||||
return domain.get_type(kind, name, version)
|
|
||||||
|
|
||||||
# TODO: find what triggers a "DefaultUndefinedFault" and implement it
|
|
||||||
# (didn't found in boto source code, nor in the docs, nor on a Google search)
|
|
||||||
# (will try to reach support)
|
|
||||||
def start_workflow_execution(self, domain_name, workflow_id,
|
|
||||||
workflow_name, workflow_version,
|
|
||||||
tag_list=None, **kwargs):
|
|
||||||
self._check_string(domain_name)
|
|
||||||
self._check_string(workflow_id)
|
|
||||||
self._check_string(workflow_name)
|
|
||||||
self._check_string(workflow_version)
|
|
||||||
self._check_none_or_list_of_strings(tag_list)
|
|
||||||
for _, value in kwargs.iteritems():
|
|
||||||
self._check_none_or_string(value)
|
|
||||||
|
|
||||||
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(wf_type, workflow_id,
|
|
||||||
tag_list=tag_list, **kwargs)
|
|
||||||
domain.add_workflow_execution(wfe)
|
|
||||||
|
|
||||||
return wfe
|
|
||||||
|
|
||||||
def describe_workflow_execution(self, domain_name, run_id, workflow_id):
|
|
||||||
self._check_string(domain_name)
|
|
||||||
self._check_string(run_id)
|
|
||||||
self._check_string(workflow_id)
|
|
||||||
domain = self._get_domain(domain_name)
|
|
||||||
return domain.get_workflow_execution(run_id, workflow_id)
|
|
||||||
|
|
||||||
|
|
||||||
swf_backends = {}
|
|
||||||
for region in boto.swf.regions():
|
|
||||||
swf_backends[region.name] = SWFBackend(region.name)
|
|
166
moto/swf/models/__init__.py
Normal file
166
moto/swf/models/__init__.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import boto.swf
|
||||||
|
|
||||||
|
from moto.core import BaseBackend
|
||||||
|
|
||||||
|
from ..exceptions import (
|
||||||
|
SWFUnknownResourceFault,
|
||||||
|
SWFDomainAlreadyExistsFault,
|
||||||
|
SWFDomainDeprecatedFault,
|
||||||
|
SWFSerializationException,
|
||||||
|
SWFTypeAlreadyExistsFault,
|
||||||
|
SWFTypeDeprecatedFault,
|
||||||
|
)
|
||||||
|
from .activity_type import ActivityType
|
||||||
|
from .domain import Domain
|
||||||
|
from .generic_type import GenericType
|
||||||
|
from .workflow_type import WorkflowType
|
||||||
|
from .workflow_execution import WorkflowExecution
|
||||||
|
|
||||||
|
|
||||||
|
class SWFBackend(BaseBackend):
|
||||||
|
def __init__(self, region_name):
|
||||||
|
self.region_name = region_name
|
||||||
|
self.domains = []
|
||||||
|
super(SWFBackend, self).__init__()
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
region_name = self.region_name
|
||||||
|
self.__dict__ = {}
|
||||||
|
self.__init__(region_name)
|
||||||
|
|
||||||
|
def _get_domain(self, name, ignore_empty=False):
|
||||||
|
matching = [domain for domain in self.domains if domain.name == name]
|
||||||
|
if not matching and not ignore_empty:
|
||||||
|
raise SWFUnknownResourceFault("domain", name)
|
||||||
|
if matching:
|
||||||
|
return matching[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _check_none_or_string(self, parameter):
|
||||||
|
if parameter is not None:
|
||||||
|
self._check_string(parameter)
|
||||||
|
|
||||||
|
def _check_string(self, parameter):
|
||||||
|
if not isinstance(parameter, basestring):
|
||||||
|
raise SWFSerializationException(parameter)
|
||||||
|
|
||||||
|
def _check_none_or_list_of_strings(self, parameter):
|
||||||
|
if parameter is not None:
|
||||||
|
self._check_list_of_strings(parameter)
|
||||||
|
|
||||||
|
def _check_list_of_strings(self, parameter):
|
||||||
|
if not isinstance(parameter, list):
|
||||||
|
raise SWFSerializationException(parameter)
|
||||||
|
for i in parameter:
|
||||||
|
if not isinstance(i, basestring):
|
||||||
|
raise SWFSerializationException(parameter)
|
||||||
|
|
||||||
|
def list_domains(self, status, reverse_order=None):
|
||||||
|
self._check_string(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 register_domain(self, name, workflow_execution_retention_period_in_days,
|
||||||
|
description=None):
|
||||||
|
self._check_string(name)
|
||||||
|
self._check_string(workflow_execution_retention_period_in_days)
|
||||||
|
self._check_none_or_string(description)
|
||||||
|
if self._get_domain(name, ignore_empty=True):
|
||||||
|
raise SWFDomainAlreadyExistsFault(name)
|
||||||
|
domain = Domain(name, workflow_execution_retention_period_in_days,
|
||||||
|
description)
|
||||||
|
self.domains.append(domain)
|
||||||
|
|
||||||
|
def deprecate_domain(self, name):
|
||||||
|
self._check_string(name)
|
||||||
|
domain = self._get_domain(name)
|
||||||
|
if domain.status == "DEPRECATED":
|
||||||
|
raise SWFDomainDeprecatedFault(name)
|
||||||
|
domain.status = "DEPRECATED"
|
||||||
|
|
||||||
|
def describe_domain(self, name):
|
||||||
|
self._check_string(name)
|
||||||
|
return self._get_domain(name)
|
||||||
|
|
||||||
|
def list_types(self, kind, domain_name, status, reverse_order=None):
|
||||||
|
self._check_string(domain_name)
|
||||||
|
self._check_string(status)
|
||||||
|
domain = self._get_domain(domain_name)
|
||||||
|
_types = domain.find_types(kind, status)
|
||||||
|
_types = sorted(_types, key=lambda domain: domain.name)
|
||||||
|
if reverse_order:
|
||||||
|
_types = reversed(_types)
|
||||||
|
return _types
|
||||||
|
|
||||||
|
def register_type(self, kind, domain_name, name, version, **kwargs):
|
||||||
|
self._check_string(domain_name)
|
||||||
|
self._check_string(name)
|
||||||
|
self._check_string(version)
|
||||||
|
for _, value in kwargs.iteritems():
|
||||||
|
self._check_none_or_string(value)
|
||||||
|
domain = self._get_domain(domain_name)
|
||||||
|
_type = domain.get_type(kind, name, version, ignore_empty=True)
|
||||||
|
if _type:
|
||||||
|
raise SWFTypeAlreadyExistsFault(_type)
|
||||||
|
_class = globals()["{}Type".format(kind.capitalize())]
|
||||||
|
_type = _class(name, version, **kwargs)
|
||||||
|
domain.add_type(_type)
|
||||||
|
|
||||||
|
def deprecate_type(self, kind, domain_name, name, version):
|
||||||
|
self._check_string(domain_name)
|
||||||
|
self._check_string(name)
|
||||||
|
self._check_string(version)
|
||||||
|
domain = self._get_domain(domain_name)
|
||||||
|
_type = domain.get_type(kind, name, version)
|
||||||
|
if _type.status == "DEPRECATED":
|
||||||
|
raise SWFTypeDeprecatedFault(_type)
|
||||||
|
_type.status = "DEPRECATED"
|
||||||
|
|
||||||
|
def describe_type(self, kind, domain_name, name, version):
|
||||||
|
self._check_string(domain_name)
|
||||||
|
self._check_string(name)
|
||||||
|
self._check_string(version)
|
||||||
|
domain = self._get_domain(domain_name)
|
||||||
|
return domain.get_type(kind, name, version)
|
||||||
|
|
||||||
|
# TODO: find what triggers a "DefaultUndefinedFault" and implement it
|
||||||
|
# (didn't found in boto source code, nor in the docs, nor on a Google search)
|
||||||
|
# (will try to reach support)
|
||||||
|
def start_workflow_execution(self, domain_name, workflow_id,
|
||||||
|
workflow_name, workflow_version,
|
||||||
|
tag_list=None, **kwargs):
|
||||||
|
self._check_string(domain_name)
|
||||||
|
self._check_string(workflow_id)
|
||||||
|
self._check_string(workflow_name)
|
||||||
|
self._check_string(workflow_version)
|
||||||
|
self._check_none_or_list_of_strings(tag_list)
|
||||||
|
for _, value in kwargs.iteritems():
|
||||||
|
self._check_none_or_string(value)
|
||||||
|
|
||||||
|
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(wf_type, workflow_id,
|
||||||
|
tag_list=tag_list, **kwargs)
|
||||||
|
domain.add_workflow_execution(wfe)
|
||||||
|
|
||||||
|
return wfe
|
||||||
|
|
||||||
|
def describe_workflow_execution(self, domain_name, run_id, workflow_id):
|
||||||
|
self._check_string(domain_name)
|
||||||
|
self._check_string(run_id)
|
||||||
|
self._check_string(workflow_id)
|
||||||
|
domain = self._get_domain(domain_name)
|
||||||
|
return domain.get_workflow_execution(run_id, workflow_id)
|
||||||
|
|
||||||
|
|
||||||
|
swf_backends = {}
|
||||||
|
for region in boto.swf.regions():
|
||||||
|
swf_backends[region.name] = SWFBackend(region.name)
|
16
moto/swf/models/activity_type.py
Normal file
16
moto/swf/models/activity_type.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from .generic_type import GenericType
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityType(GenericType):
|
||||||
|
@property
|
||||||
|
def _configuration_keys(self):
|
||||||
|
return [
|
||||||
|
"defaultTaskHeartbeatTimeout",
|
||||||
|
"defaultTaskScheduleToCloseTimeout",
|
||||||
|
"defaultTaskScheduleToStartTimeout",
|
||||||
|
"defaultTaskStartToCloseTimeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kind(self):
|
||||||
|
return "activity"
|
85
moto/swf/models/domain.py
Normal file
85
moto/swf/models/domain.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from ..exceptions import (
|
||||||
|
SWFUnknownResourceFault,
|
||||||
|
SWFWorkflowExecutionAlreadyStartedFault,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Domain(object):
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
# 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
|
||||||
|
# of "workflow_id (client determined)" => WorkflowExecution()
|
||||||
|
# here.
|
||||||
|
self.workflow_executions = {}
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Domain(name: %(name)s, status: %(status)s)" % self.__dict__
|
||||||
|
|
||||||
|
def to_short_dict(self):
|
||||||
|
hsh = {
|
||||||
|
"name": self.name,
|
||||||
|
"status": self.status,
|
||||||
|
}
|
||||||
|
if self.description:
|
||||||
|
hsh["description"] = self.description
|
||||||
|
return hsh
|
||||||
|
|
||||||
|
def to_full_dict(self):
|
||||||
|
return {
|
||||||
|
"domainInfo": self.to_short_dict(),
|
||||||
|
"configuration": {
|
||||||
|
"workflowExecutionRetentionPeriodInDays": self.retention,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_type(self, kind, name, version, ignore_empty=False):
|
||||||
|
try:
|
||||||
|
return self.types[kind][name][version]
|
||||||
|
except KeyError:
|
||||||
|
if not ignore_empty:
|
||||||
|
raise SWFUnknownResourceFault(
|
||||||
|
"type",
|
||||||
|
"{}Type=[name={}, version={}]".format(
|
||||||
|
kind.capitalize(), name, version
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_type(self, _type):
|
||||||
|
self.types[_type.kind][_type.name][_type.version] = _type
|
||||||
|
|
||||||
|
def find_types(self, kind, status):
|
||||||
|
_all = []
|
||||||
|
for _, family in self.types[kind].iteritems():
|
||||||
|
for _, _type in family.iteritems():
|
||||||
|
if _type.status == status:
|
||||||
|
_all.append(_type)
|
||||||
|
return _all
|
||||||
|
|
||||||
|
def add_workflow_execution(self, workflow_execution):
|
||||||
|
_id = workflow_execution.workflow_id
|
||||||
|
if self.workflow_executions.get(_id):
|
||||||
|
raise SWFWorkflowExecutionAlreadyStartedFault()
|
||||||
|
self.workflow_executions[_id] = workflow_execution
|
||||||
|
|
||||||
|
def get_workflow_execution(self, run_id, workflow_id):
|
||||||
|
wfe = self.workflow_executions.get(workflow_id)
|
||||||
|
if not wfe or wfe.run_id != run_id:
|
||||||
|
raise SWFUnknownResourceFault(
|
||||||
|
"execution",
|
||||||
|
"WorkflowExecution=[workflowId={}, runId={}]".format(
|
||||||
|
workflow_id, run_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return wfe
|
61
moto/swf/models/generic_type.py
Normal file
61
moto/swf/models/generic_type.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from moto.core.utils import camelcase_to_underscores
|
||||||
|
|
||||||
|
|
||||||
|
class GenericType(object):
|
||||||
|
def __init__(self, name, version, **kwargs):
|
||||||
|
self.name = name
|
||||||
|
self.version = version
|
||||||
|
self.status = "REGISTERED"
|
||||||
|
if "description" in kwargs:
|
||||||
|
self.description = kwargs.pop("description")
|
||||||
|
for key, value in kwargs.iteritems():
|
||||||
|
self.__setattr__(key, value)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
cls = self.__class__.__name__
|
||||||
|
attrs = "name: %(name)s, version: %(version)s, status: %(status)s" % self.__dict__
|
||||||
|
return "{}({})".format(cls, attrs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kind(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _configuration_keys(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def to_short_dict(self):
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"version": self.version,
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_medium_dict(self):
|
||||||
|
hsh = {
|
||||||
|
"{}Type".format(self.kind): self.to_short_dict(),
|
||||||
|
"creationDate": 1420066800,
|
||||||
|
"status": self.status,
|
||||||
|
}
|
||||||
|
if self.status == "DEPRECATED":
|
||||||
|
hsh["deprecationDate"] = 1422745200
|
||||||
|
if hasattr(self, "description"):
|
||||||
|
hsh["description"] = self.description
|
||||||
|
return hsh
|
||||||
|
|
||||||
|
def to_full_dict(self):
|
||||||
|
hsh = {
|
||||||
|
"typeInfo": self.to_medium_dict(),
|
||||||
|
"configuration": {}
|
||||||
|
}
|
||||||
|
if hasattr(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)
|
||||||
|
return hsh
|
72
moto/swf/models/workflow_execution.py
Normal file
72
moto/swf/models/workflow_execution.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from moto.core.utils import camelcase_to_underscores
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowExecution(object):
|
||||||
|
def __init__(self, workflow_type, workflow_id, **kwargs):
|
||||||
|
self.workflow_type = workflow_type
|
||||||
|
self.workflow_id = workflow_id
|
||||||
|
self.run_id = uuid.uuid4().hex
|
||||||
|
self.execution_status = "OPEN"
|
||||||
|
self.cancel_requested = False
|
||||||
|
#config
|
||||||
|
for key, value in kwargs.iteritems():
|
||||||
|
self.__setattr__(key, value)
|
||||||
|
#counters
|
||||||
|
self.open_counts = {
|
||||||
|
"openTimers": 0,
|
||||||
|
"openDecisionTasks": 0,
|
||||||
|
"openActivityTasks": 0,
|
||||||
|
"openChildWorkflowExecutions": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "WorkflowExecution(run_id: {})".format(self.run_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _configuration_keys(self):
|
||||||
|
return [
|
||||||
|
"executionStartToCloseTimeout",
|
||||||
|
"childPolicy",
|
||||||
|
"taskPriority",
|
||||||
|
"taskStartToCloseTimeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
def to_short_dict(self):
|
||||||
|
return {
|
||||||
|
"workflowId": self.workflow_id,
|
||||||
|
"runId": self.run_id
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_medium_dict(self):
|
||||||
|
hsh = {
|
||||||
|
"execution": self.to_short_dict(),
|
||||||
|
"workflowType": self.workflow_type.to_short_dict(),
|
||||||
|
"startTimestamp": 1420066800.123,
|
||||||
|
"executionStatus": self.execution_status,
|
||||||
|
"cancelRequested": self.cancel_requested,
|
||||||
|
}
|
||||||
|
if hasattr(self, "tag_list"):
|
||||||
|
hsh["tagList"] = self.tag_list
|
||||||
|
return hsh
|
||||||
|
|
||||||
|
def to_full_dict(self):
|
||||||
|
hsh = {
|
||||||
|
"executionInfo": self.to_medium_dict(),
|
||||||
|
"executionConfiguration": {}
|
||||||
|
}
|
||||||
|
#configuration
|
||||||
|
if hasattr(self, "task_list"):
|
||||||
|
hsh["executionConfiguration"]["taskList"] = {"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["executionConfiguration"][key] = getattr(self, attr)
|
||||||
|
#counters
|
||||||
|
hsh["openCounts"] = self.open_counts
|
||||||
|
return hsh
|
15
moto/swf/models/workflow_type.py
Normal file
15
moto/swf/models/workflow_type.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from .generic_type import GenericType
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowType(GenericType):
|
||||||
|
@property
|
||||||
|
def _configuration_keys(self):
|
||||||
|
return [
|
||||||
|
"defaultChildPolicy",
|
||||||
|
"defaultExecutionStartToCloseTimeout",
|
||||||
|
"defaultTaskStartToCloseTimeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kind(self):
|
||||||
|
return "workflow"
|
Loading…
x
Reference in New Issue
Block a user