diff --git a/moto/swf/models.py b/moto/swf/models.py index 8d5d8f0e..421e911c 100644 --- a/moto/swf/models.py +++ b/moto/swf/models.py @@ -4,6 +4,8 @@ from collections import defaultdict import boto.swf from moto.core import BaseBackend +from moto.core.utils import camelcase_to_underscores + from .exceptions import ( SWFUnknownResourceFault, SWFDomainAlreadyExistsFault, @@ -26,6 +28,15 @@ class Domain(object): def __repr__(self): return "Domain(name: %(name)s, status: %(status)s)" % self.__dict__ + def to_dict(self): + hsh = { + "name": self.name, + "status": self.status, + } + if self.description: + hsh["description"] = self.description + return hsh + def get_type(self, kind, name, version, ignore_empty=False): try: _types = getattr(self, "{}_types".format(kind)) @@ -62,12 +73,57 @@ class ActivityType(object): 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): return "ActivityType(name: %(name)s, version: %(version)s)" % self.__dict__ + @property + def _configuration_keys(self): + return [ + "defaultTaskHeartbeatTimeout", + "defaultTaskScheduleToCloseTimeout", + "defaultTaskScheduleToStartTimeout", + "defaultTaskStartToCloseTimeout", + ] + + def to_short_dict(self): + return { + "name": self.name, + "version": self.version, + } + + def to_medium_dict(self): + hsh = { + "activityType": 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 WorkflowType(object): def __init__(self, name, version, **kwargs): @@ -80,6 +136,48 @@ class WorkflowType(object): def __repr__(self): return "WorkflowType(name: %(name)s, version: %(version)s)" % self.__dict__ + @property + def _configuration_keys(self): + return [ + "defaultChildPolicy", + "defaultExecutionStartToCloseTimeout", + "defaultTaskStartToCloseTimeout", + ] + + def to_short_dict(self): + return { + "name": self.name, + "version": self.version, + } + + def to_medium_dict(self): + hsh = { + "workflowType": 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 getattr(self, attr) is None: + continue + hsh["configuration"][key] = getattr(self, attr) + return hsh + class SWFBackend(BaseBackend): def __init__(self, region_name): diff --git a/moto/swf/responses.py b/moto/swf/responses.py index a551aab2..82c50565 100644 --- a/moto/swf/responses.py +++ b/moto/swf/responses.py @@ -51,23 +51,24 @@ class SWFResponse(BaseResponse): def _params(self): return json.loads(self.body) - def _list_types(self, kind, template_str): + def _list_types(self, kind): domain_name = self._params.get("domain") status = self._params.get("registrationStatus") reverse_order = self._params.get("reverseOrder", None) types = self.swf_backend.list_types(kind, domain_name, status, reverse_order=reverse_order) - template = self.response_template(template_str) - return template.render(types=types) + return json.dumps({ + "typeInfos": [_type.to_medium_dict() for _type in types] + }) - def _describe_type(self, kind, template_str): + def _describe_type(self, kind): domain = self._params.get("domain") - _type = self._params.get("{}Type".format(kind)) + _type_args = self._params.get("{}Type".format(kind)) - name = _type["name"] - version = _type["version"] + name = _type_args["name"] + version = _type_args["version"] _type = self.swf_backend.describe_type(kind, domain, name, version) - template = self.response_template(template_str) - return template.render(_type=_type) + + return json.dumps(_type.to_full_dict()) def _deprecate_type(self, kind): domain = self._params.get("domain") @@ -83,8 +84,9 @@ class SWFResponse(BaseResponse): status = self._params.get("registrationStatus") reverse_order = self._params.get("reverseOrder", None) domains = self.swf_backend.list_domains(status, reverse_order=reverse_order) - template = self.response_template(LIST_DOMAINS_TEMPLATE) - return template.render(domains=domains) + return json.dumps({ + "domainInfos": [domain.to_dict() for domain in domains] + }) def register_domain(self): name = self._params.get("name") @@ -92,24 +94,24 @@ class SWFResponse(BaseResponse): retention = self._params.get("workflowExecutionRetentionPeriodInDays") domain = self.swf_backend.register_domain(name, retention, description=description) - template = self.response_template("") - return template.render() + return "" def deprecate_domain(self): name = self._params.get("name") domain = self.swf_backend.deprecate_domain(name) - template = self.response_template("") - return template.render() + return "" def describe_domain(self): name = self._params.get("name") domain = self.swf_backend.describe_domain(name) - template = self.response_template(DESCRIBE_DOMAIN_TEMPLATE) - return template.render(domain=domain) + return json.dumps({ + "configuration": { "workflowExecutionRetentionPeriodInDays": domain.retention }, + "domainInfo": domain.to_dict() + }) # TODO: implement pagination def list_activity_types(self): - return self._list_types("activity", LIST_ACTIVITY_TYPES_TEMPLATE) + return self._list_types("activity") def register_activity_type(self): domain = self._params.get("domain") @@ -141,11 +143,11 @@ class SWFResponse(BaseResponse): return self._deprecate_type("activity") def describe_activity_type(self): - return self._describe_type("activity", DESCRIBE_ACTIVITY_TYPE_TEMPLATE) + return self._describe_type("activity") # TODO: refactor with list_activity_types() def list_workflow_types(self): - return self._list_types("workflow", LIST_WORKFLOW_TYPES_TEMPLATE) + return self._list_types("workflow") def register_workflow_type(self): domain = self._params.get("domain") @@ -176,88 +178,4 @@ class SWFResponse(BaseResponse): return self._deprecate_type("workflow") def describe_workflow_type(self): - return self._describe_type("workflow", DESCRIBE_WORKFLOW_TYPE_TEMPLATE) - - -LIST_DOMAINS_TEMPLATE = """{ - "domainInfos": [ - {%- for domain in domains %} - { - "description": "{{ domain.description }}", - "name": "{{ domain.name }}", - "status": "{{ domain.status }}" - }{% if not loop.last %},{% endif %} - {%- endfor %} - ] -}""" - -DESCRIBE_DOMAIN_TEMPLATE = """{ - "configuration": { - "workflowExecutionRetentionPeriodInDays": "{{ domain.retention }}" - }, - "domainInfo": { - "description": "{{ domain.description }}", - "name": "{{ domain.name }}", - "status": "{{ domain.status }}" - } -}""" - -LIST_ACTIVITY_TYPES_TEMPLATE = """{ - "typeInfos": [ - {%- for _type in types %} - { - "activityType": { - "name": "{{ _type.name }}", - "version": "{{ _type.version }}" - }, - "creationDate": 1420066800, - {% if _type.status == "DEPRECATED" %}"deprecationDate": 1422745200,{% endif %} - {% if _type.description %}"description": "{{ _type.description }}",{% endif %} - "status": "{{ _type.status }}" - }{% if not loop.last %},{% endif %} - {%- endfor %} - ] -}""" - -DESCRIBE_ACTIVITY_TYPE_TEMPLATE = """{ - "configuration": { - {% if _type.default_task_heartbeat_timeout %}"defaultTaskHeartbeatTimeout": "{{ _type.default_task_heartbeat_timeout }}",{% endif %} - {% if _type.task_list %}"defaultTaskList": { "name": "{{ _type.task_list }}" },{% endif %} - {% if _type.default_task_schedule_to_close_timeout %}"defaultTaskScheduleToCloseTimeout": "{{ _type.default_task_schedule_to_close_timeout }}",{% endif %} - {% if _type.default_task_schedule_to_start_timeout %}"defaultTaskScheduleToStartTimeout": "{{ _type.default_task_schedule_to_start_timeout }}",{% endif %} - {% if _type.default_task_start_to_close_timeout %}"defaultTaskStartToCloseTimeout": "{{ _type.default_task_start_to_close_timeout }}",{% endif %} - "__moto_placeholder": "(avoid dealing with coma in json)" - }, - "typeInfo": { - "activityType": { - "name": "{{ _type.name }}", - "version": "{{ _type.version }}" - }, - "creationDate": 1420066800, - {% if _type.status == "DEPRECATED" %}"deprecationDate": 1422745200,{% endif %} - {% if _type.description %}"description": "{{ _type.description }}",{% endif %} - "status": "{{ _type.status }}" - } -}""" - -LIST_WORKFLOW_TYPES_TEMPLATE = LIST_ACTIVITY_TYPES_TEMPLATE.replace("activityType", "workflowType") - -DESCRIBE_WORKFLOW_TYPE_TEMPLATE = """{ - "configuration": { - {% if _type.default_child_policy %}"defaultChildPolicy": "{{ _type.default_child_policy }}",{% endif %} - {% if _type.default_execution_start_to_close_timeout %}"defaultExecutionStartToCloseTimeout": "{{ _type.default_execution_start_to_close_timeout }}",{% endif %} - {% if _type.task_list %}"defaultTaskList": { "name": "{{ _type.task_list }}" },{% endif %} - {% if _type.default_task_start_to_close_timeout %}"defaultTaskStartToCloseTimeout": "{{ _type.default_task_start_to_close_timeout }}",{% endif %} - "__moto_placeholder": "(avoid dealing with coma in json)" - }, - "typeInfo": { - "workflowType": { - "name": "{{ _type.name }}", - "version": "{{ _type.version }}" - }, - "creationDate": 1420066800, - {% if _type.status == "DEPRECATED" %}"deprecationDate": 1422745200,{% endif %} - {% if _type.description %}"description": "{{ _type.description }}",{% endif %} - "status": "{{ _type.status }}" - } -}""" + return self._describe_type("workflow") diff --git a/tests/test_swf/test_activity_types.py b/tests/test_swf/test_activity_types.py index 19f586a0..567b0fa0 100644 --- a/tests/test_swf/test_activity_types.py +++ b/tests/test_swf/test_activity_types.py @@ -3,6 +3,7 @@ from nose.tools import assert_raises from sure import expect from moto import mock_swf +from moto.swf.models import ActivityType from moto.swf.exceptions import ( SWFUnknownResourceFault, SWFTypeAlreadyExistsFault, @@ -11,6 +12,37 @@ from moto.swf.exceptions import ( ) +# Models +def test_short_dict_representation(): + _type = ActivityType("test-activity", "v1.0") + _type.to_short_dict().should.equal({"name": "test-activity", "version": "v1.0"}) + +def test_medium_dict_representation(): + _type = ActivityType("test-activity", "v1.0") + _type.to_medium_dict()["activityType"].should.equal(_type.to_short_dict()) + _type.to_medium_dict()["status"].should.equal("REGISTERED") + _type.to_medium_dict().should.contain("creationDate") + _type.to_medium_dict().should_not.contain("deprecationDate") + _type.to_medium_dict().should_not.contain("description") + + _type.description = "foo bar" + _type.to_medium_dict()["description"].should.equal("foo bar") + + _type.status = "DEPRECATED" + _type.to_medium_dict().should.contain("deprecationDate") + +def test_full_dict_representation(): + _type = ActivityType("test-activity", "v1.0") + _type.to_full_dict()["typeInfo"].should.equal(_type.to_medium_dict()) + _type.to_full_dict()["configuration"].should.equal({}) + + _type.task_list = "foo" + _type.to_full_dict()["configuration"]["defaultTaskList"].should.equal({"name":"foo"}) + + _type.default_task_heartbeat_timeout = "60" + _type.to_full_dict()["configuration"]["defaultTaskHeartbeatTimeout"].should.equal("60") + + # RegisterActivityType endpoint @mock_swf def test_register_activity_type(): @@ -138,7 +170,6 @@ def test_describe_activity_type(): actype = conn.describe_activity_type("test-domain", "test-activity", "v1.0") actype["configuration"]["defaultTaskList"]["name"].should.equal("foo") - actype["configuration"].keys().should_not.contain("defaultTaskScheduleToClose") infos = actype["typeInfo"] infos["activityType"]["name"].should.equal("test-activity") infos["activityType"]["version"].should.equal("v1.0") diff --git a/tests/test_swf/test_domains.py b/tests/test_swf/test_domains.py index 8f3a7556..2e25c1ac 100644 --- a/tests/test_swf/test_domains.py +++ b/tests/test_swf/test_domains.py @@ -3,6 +3,7 @@ from nose.tools import assert_raises from sure import expect from moto import mock_swf +from moto.swf.models import Domain from moto.swf.exceptions import ( SWFUnknownResourceFault, SWFDomainAlreadyExistsFault, @@ -11,6 +12,15 @@ from moto.swf.exceptions import ( ) +# Models +def test_dict_representation(): + domain = Domain("foo", "52") + domain.to_dict().should.equal({"name":"foo", "status":"REGISTERED"}) + + domain.description = "foo bar" + domain.to_dict()["description"].should.equal("foo bar") + + # RegisterDomain endpoint @mock_swf def test_register_domain(): @@ -53,7 +63,7 @@ def test_register_with_wrong_parameter_type(): ex.body["__type"].should.equal("com.amazonaws.swf.base.model#SerializationException") -# ListDomain endpoint +# ListDomains endpoint @mock_swf def test_list_domains_order(): conn = boto.connect_swf("the_key", "the_secret")