From 2878252816d85692ead417b7fd1820a72ea5eccb Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Barth Date: Fri, 2 Oct 2015 17:42:28 +0200 Subject: [PATCH] Add SWF endpoint: DescribeWorkflowExecution --- moto/swf/models.py | 74 ++++++++++++++++++++++ moto/swf/responses.py | 10 ++- tests/test_swf/test_models.py | 42 ++++++++++++ tests/test_swf/test_workflow_executions.py | 28 ++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) diff --git a/moto/swf/models.py b/moto/swf/models.py index dc29dc664..253336be0 100644 --- a/moto/swf/models.py +++ b/moto/swf/models.py @@ -84,6 +84,17 @@ class Domain(object): 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): @@ -177,12 +188,68 @@ class WorkflowExecution(object): 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): @@ -318,6 +385,13 @@ class SWFBackend(BaseBackend): 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(): diff --git a/moto/swf/responses.py b/moto/swf/responses.py index 105db4c06..b4e349931 100644 --- a/moto/swf/responses.py +++ b/moto/swf/responses.py @@ -139,7 +139,6 @@ class SWFResponse(BaseResponse): def describe_activity_type(self): return self._describe_type("activity") - # TODO: refactor with list_activity_types() def list_workflow_types(self): return self._list_types("workflow") @@ -201,3 +200,12 @@ class SWFResponse(BaseResponse): return json.dumps({ "runId": wfe.run_id }) + + def describe_workflow_execution(self): + domain_name = self._params["domain"] + _workflow_execution = self._params["execution"] + run_id = _workflow_execution["runId"] + workflow_id = _workflow_execution["workflowId"] + + wfe = self.swf_backend.describe_workflow_execution(domain_name, run_id, workflow_id) + return json.dumps(wfe.to_full_dict()) diff --git a/tests/test_swf/test_models.py b/tests/test_swf/test_models.py index 33006cbf7..bf6437f5b 100644 --- a/tests/test_swf/test_models.py +++ b/tests/test_swf/test_models.py @@ -3,6 +3,7 @@ from sure import expect from moto.swf.models import ( Domain, GenericType, + WorkflowType, WorkflowExecution, ) @@ -89,3 +90,44 @@ def test_workflow_execution_generates_a_random_run_id(): wfe1 = WorkflowExecution("workflow_type_whatever", "ab1234") wfe2 = WorkflowExecution("workflow_type_whatever", "ab1235") wfe1.run_id.should_not.equal(wfe2.run_id) + +def test_workflow_execution_short_dict_representation(): + wf_type = WorkflowType("test-workflow", "v1.0") + wfe = WorkflowExecution(wf_type, "ab1234") + + sd = wfe.to_short_dict() + sd["workflowId"].should.equal("ab1234") + sd.should.contain("runId") + +def test_workflow_execution_medium_dict_representation(): + wf_type = WorkflowType("test-workflow", "v1.0") + wfe = WorkflowExecution(wf_type, "ab1234") + + md = wfe.to_medium_dict() + md["execution"].should.equal(wfe.to_short_dict()) + md["workflowType"].should.equal(wf_type.to_short_dict()) + md["startTimestamp"].should.be.a('float') + md["executionStatus"].should.equal("OPEN") + md["cancelRequested"].should.equal(False) + md.should_not.contain("tagList") + + wfe.tag_list = ["foo", "bar", "baz"] + md = wfe.to_medium_dict() + md["tagList"].should.equal(["foo", "bar", "baz"]) + +def test_workflow_execution_full_dict_representation(): + wf_type = WorkflowType("test-workflow", "v1.0") + wfe = WorkflowExecution(wf_type, "ab1234") + + fd = wfe.to_full_dict() + fd["executionInfo"].should.equal(wfe.to_medium_dict()) + fd["openCounts"]["openTimers"].should.equal(0) + fd["openCounts"]["openDecisionTasks"].should.equal(0) + fd["openCounts"]["openActivityTasks"].should.equal(0) + fd["executionConfiguration"].should.equal({}) + + wfe.task_list = "special" + wfe.task_start_to_close_timeout = "45" + fd = wfe.to_full_dict() + fd["executionConfiguration"]["taskList"]["name"].should.equal("special") + fd["executionConfiguration"]["taskStartToCloseTimeout"].should.equal("45") diff --git a/tests/test_swf/test_workflow_executions.py b/tests/test_swf/test_workflow_executions.py index 930fba538..4082ab540 100644 --- a/tests/test_swf/test_workflow_executions.py +++ b/tests/test_swf/test_workflow_executions.py @@ -6,6 +6,7 @@ from moto import mock_swf from moto.swf.exceptions import ( SWFWorkflowExecutionAlreadyStartedFault, SWFTypeDeprecatedFault, + SWFUnknownResourceFault, ) @@ -57,3 +58,30 @@ def test_start_workflow_execution_on_deprecated_type(): "__type": "com.amazonaws.swf.base.model#TypeDeprecatedFault", "message": "WorkflowType=[name=test-workflow, version=v1.0]" }) + + +# DescribeWorkflowExecution endpoint +@mock_swf +def test_describe_workflow_execution(): + conn = setup_swf_environment() + hsh = conn.start_workflow_execution("test-domain", "uid-abcd1234", "test-workflow", "v1.0") + run_id = hsh["runId"] + + wfe = conn.describe_workflow_execution("test-domain", run_id, "uid-abcd1234") + wfe["executionInfo"]["execution"]["workflowId"].should.equal("uid-abcd1234") + wfe["executionInfo"]["executionStatus"].should.equal("OPEN") + +@mock_swf +def test_describe_non_existent_workflow_execution(): + conn = setup_swf_environment() + + with assert_raises(SWFUnknownResourceFault) as err: + conn.describe_workflow_execution("test-domain", "wrong-run-id", "wrong-workflow-id") + + ex = err.exception + ex.status.should.equal(400) + ex.error_code.should.equal("UnknownResourceFault") + ex.body.should.equal({ + "__type": "com.amazonaws.swf.base.model#UnknownResourceFault", + "message": "Unknown execution: WorkflowExecution=[workflowId=wrong-workflow-id, runId=wrong-run-id]" + })