diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index a863d483d..a22cc3bfb 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -7272,9 +7272,9 @@
- [X] start_workflow_execution
- [ ] tag_resource
- [X] terminate_workflow_execution
-- [ ] undeprecate_activity_type
-- [ ] undeprecate_domain
-- [ ] undeprecate_workflow_type
+- [X] undeprecate_activity_type
+- [X] undeprecate_domain
+- [X] undeprecate_workflow_type
- [ ] untag_resource
## textract
diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py
index 88f750775..8e5a61755 100644
--- a/moto/dynamodb2/models.py
+++ b/moto/dynamodb2/models.py
@@ -450,9 +450,9 @@ class Item(BaseModel):
old_list_key = list_append_re.group(1)
# old_key could be a function itself (if_not_exists)
if old_list_key.startswith("if_not_exists"):
- old_list = DynamoType(
- expression_attribute_values[self._get_default(old_list_key)]
- )
+ old_list = self._get_default(old_list_key)
+ if not isinstance(old_list, DynamoType):
+ old_list = DynamoType(expression_attribute_values[old_list])
else:
old_list = self.attrs[old_list_key.split(".")[0]]
if "." in old_list_key:
diff --git a/moto/route53/models.py b/moto/route53/models.py
index 2ae03e54d..0bdefd25b 100644
--- a/moto/route53/models.py
+++ b/moto/route53/models.py
@@ -88,6 +88,8 @@ class RecordSet(BaseModel):
self.hosted_zone_name = kwargs.get("HostedZoneName")
self.hosted_zone_id = kwargs.get("HostedZoneId")
self.alias_target = kwargs.get("AliasTarget")
+ self.failover = kwargs.get("Failover")
+ self.geo_location = kwargs.get("GeoLocation")
@classmethod
def create_from_cloudformation_json(
@@ -154,6 +156,16 @@ class RecordSet(BaseModel):
{% if record_set.ttl %}
{{ record_set.ttl }}
{% endif %}
+ {% if record_set.failover %}
+ {{ record_set.failover }}
+ {% endif %}
+ {% if record_set.geo_location %}
+
+ {% for geo_key in ['ContinentCode','CountryCode','SubdivisionCode'] %}
+ {% if record_set.geo_location[geo_key] %}<{{ geo_key }}>{{ record_set.geo_location[geo_key] }}{{ geo_key }}>{% endif %}
+ {% endfor %}
+
+ {% endif %}
{% if record_set.alias_target %}
{{ record_set.alias_target['HostedZoneId'] }}
diff --git a/moto/ssm/models.py b/moto/ssm/models.py
index 60c47f021..a7518d405 100644
--- a/moto/ssm/models.py
+++ b/moto/ssm/models.py
@@ -278,10 +278,7 @@ class SimpleSystemManagerBackend(BaseBackend):
self._region = region
def delete_parameter(self, name):
- try:
- del self._parameters[name]
- except KeyError:
- pass
+ return self._parameters.pop(name, None)
def delete_parameters(self, names):
result = []
diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py
index 1b13780a8..831737848 100644
--- a/moto/ssm/responses.py
+++ b/moto/ssm/responses.py
@@ -22,7 +22,13 @@ class SimpleSystemManagerResponse(BaseResponse):
def delete_parameter(self):
name = self._get_param("Name")
- self.ssm_backend.delete_parameter(name)
+ result = self.ssm_backend.delete_parameter(name)
+ if result is None:
+ error = {
+ "__type": "ParameterNotFound",
+ "message": "Parameter {0} not found.".format(name),
+ }
+ return json.dumps(error), dict(status=400)
return json.dumps({})
def delete_parameters(self):
diff --git a/moto/swf/models/__init__.py b/moto/swf/models/__init__.py
index e5b285f5b..010c8c734 100644
--- a/moto/swf/models/__init__.py
+++ b/moto/swf/models/__init__.py
@@ -121,6 +121,12 @@ class SWFBackend(BaseBackend):
raise SWFDomainDeprecatedFault(name)
domain.status = "DEPRECATED"
+ def undeprecate_domain(self, name):
+ domain = self._get_domain(name)
+ if domain.status == "REGISTERED":
+ raise SWFDomainAlreadyExistsFault(name)
+ domain.status = "REGISTERED"
+
def describe_domain(self, name):
return self._get_domain(name)
@@ -148,6 +154,13 @@ class SWFBackend(BaseBackend):
raise SWFTypeDeprecatedFault(_type)
_type.status = "DEPRECATED"
+ def undeprecate_type(self, kind, domain_name, name, version):
+ domain = self._get_domain(domain_name)
+ _type = domain.get_type(kind, name, version)
+ if _type.status == "REGISTERED":
+ raise SWFTypeAlreadyExistsFault(_type)
+ _type.status = "REGISTERED"
+
def describe_type(self, kind, domain_name, name, version):
domain = self._get_domain(domain_name)
return domain.get_type(kind, name, version)
diff --git a/moto/swf/responses.py b/moto/swf/responses.py
index c8c601fa7..17ec7281a 100644
--- a/moto/swf/responses.py
+++ b/moto/swf/responses.py
@@ -92,6 +92,17 @@ class SWFResponse(BaseResponse):
self.swf_backend.deprecate_type(kind, domain, name, version)
return ""
+ def _undeprecate_type(self, kind):
+ domain = self._params["domain"]
+ _type_args = self._params["{0}Type".format(kind)]
+ name = _type_args["name"]
+ version = _type_args["version"]
+ self._check_string(domain)
+ self._check_string(name)
+ self._check_string(version)
+ self.swf_backend.undeprecate_type(kind, domain, name, version)
+ return ""
+
# TODO: implement pagination
def list_domains(self):
status = self._params["registrationStatus"]
@@ -219,6 +230,12 @@ class SWFResponse(BaseResponse):
self.swf_backend.deprecate_domain(name)
return ""
+ def undeprecate_domain(self):
+ name = self._params["name"]
+ self._check_string(name)
+ self.swf_backend.undeprecate_domain(name)
+ return ""
+
def describe_domain(self):
name = self._params["name"]
self._check_string(name)
@@ -278,6 +295,9 @@ class SWFResponse(BaseResponse):
def deprecate_activity_type(self):
return self._deprecate_type("activity")
+ def undeprecate_activity_type(self):
+ return self._undeprecate_type("activity")
+
def describe_activity_type(self):
return self._describe_type("activity")
@@ -333,6 +353,9 @@ class SWFResponse(BaseResponse):
def deprecate_workflow_type(self):
return self._deprecate_type("workflow")
+ def undeprecate_workflow_type(self):
+ return self._undeprecate_type("workflow")
+
def describe_workflow_type(self):
return self._describe_type("workflow")
diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py
index 180f460c0..428b58f81 100644
--- a/tests/test_dynamodb2/test_dynamodb.py
+++ b/tests/test_dynamodb2/test_dynamodb.py
@@ -3634,6 +3634,31 @@ def test_update_supports_list_append_with_nested_if_not_exists_operation():
)
+@mock_dynamodb2
+def test_update_supports_list_append_with_nested_if_not_exists_operation_and_property_already_exists():
+ dynamo = boto3.resource("dynamodb", region_name="us-west-1")
+ table_name = "test"
+
+ dynamo.create_table(
+ TableName=table_name,
+ AttributeDefinitions=[{"AttributeName": "Id", "AttributeType": "S"}],
+ KeySchema=[{"AttributeName": "Id", "KeyType": "HASH"}],
+ ProvisionedThroughput={"ReadCapacityUnits": 20, "WriteCapacityUnits": 20},
+ )
+
+ table = dynamo.Table(table_name)
+
+ table.put_item(Item={"Id": "item-id", "event_history": ["other_value"]})
+ table.update_item(
+ Key={"Id": "item-id"},
+ UpdateExpression="SET event_history = list_append(if_not_exists(event_history, :empty_list), :new_value)",
+ ExpressionAttributeValues={":empty_list": [], ":new_value": ["some_value"]},
+ )
+ table.get_item(Key={"Id": "item-id"})["Item"].should.equal(
+ {"Id": "item-id", "event_history": ["other_value", "some_value"]}
+ )
+
+
@mock_dynamodb2
def test_update_catches_invalid_list_append_operation():
client = boto3.client("dynamodb", region_name="us-east-1")
diff --git a/tests/test_route53/test_route53.py b/tests/test_route53/test_route53.py
index 746c78719..8c036441c 100644
--- a/tests/test_route53/test_route53.py
+++ b/tests/test_route53/test_route53.py
@@ -753,6 +753,79 @@ def test_change_weighted_resource_record_sets():
record["Weight"].should.equal(10)
+@mock_route53
+def test_failover_record_sets():
+ conn = boto3.client("route53", region_name="us-east-2")
+ conn.create_hosted_zone(Name="test.zone.", CallerReference=str(hash("test")))
+ zones = conn.list_hosted_zones_by_name(DNSName="test.zone.")
+ hosted_zone_id = zones["HostedZones"][0]["Id"]
+
+ # Create geolocation record
+ conn.change_resource_record_sets(
+ HostedZoneId=hosted_zone_id,
+ ChangeBatch={
+ "Changes": [
+ {
+ "Action": "CREATE",
+ "ResourceRecordSet": {
+ "Name": "failover.test.zone.",
+ "Type": "A",
+ "TTL": 10,
+ "ResourceRecords": [{"Value": "127.0.0.1"}],
+ "Failover": "PRIMARY",
+ },
+ }
+ ]
+ },
+ )
+
+ response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id)
+ record = response["ResourceRecordSets"][0]
+ record["Failover"].should.equal("PRIMARY")
+
+
+@mock_route53
+def test_geolocation_record_sets():
+ conn = boto3.client("route53", region_name="us-east-2")
+ conn.create_hosted_zone(Name="test.zone.", CallerReference=str(hash("test")))
+ zones = conn.list_hosted_zones_by_name(DNSName="test.zone.")
+ hosted_zone_id = zones["HostedZones"][0]["Id"]
+
+ # Create geolocation record
+ conn.change_resource_record_sets(
+ HostedZoneId=hosted_zone_id,
+ ChangeBatch={
+ "Changes": [
+ {
+ "Action": "CREATE",
+ "ResourceRecordSet": {
+ "Name": "georecord1.test.zone.",
+ "Type": "A",
+ "TTL": 10,
+ "ResourceRecords": [{"Value": "127.0.0.1"}],
+ "GeoLocation": {"ContinentCode": "EU"},
+ },
+ },
+ {
+ "Action": "CREATE",
+ "ResourceRecordSet": {
+ "Name": "georecord2.test.zone.",
+ "Type": "A",
+ "TTL": 10,
+ "ResourceRecords": [{"Value": "127.0.0.2"}],
+ "GeoLocation": {"CountryCode": "US", "SubdivisionCode": "NY"},
+ },
+ },
+ ]
+ },
+ )
+
+ response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id)
+ rrs = response["ResourceRecordSets"]
+ rrs[0]["GeoLocation"].should.equal({"ContinentCode": "EU"})
+ rrs[1]["GeoLocation"].should.equal({"CountryCode": "US", "SubdivisionCode": "NY"})
+
+
@mock_route53
def test_change_resource_record_invalid():
conn = boto3.client("route53", region_name="us-east-1")
diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py
index 5b978520d..bb674fb65 100644
--- a/tests/test_ssm/test_ssm_boto3.py
+++ b/tests/test_ssm/test_ssm_boto3.py
@@ -30,6 +30,18 @@ def test_delete_parameter():
len(response["Parameters"]).should.equal(0)
+@mock_ssm
+def test_delete_nonexistent_parameter():
+ client = boto3.client("ssm", region_name="us-east-1")
+
+ with assert_raises(ClientError) as ex:
+ client.delete_parameter(Name="test_noexist")
+ ex.exception.response["Error"]["Code"].should.equal("ParameterNotFound")
+ ex.exception.response["Error"]["Message"].should.equal(
+ "Parameter test_noexist not found."
+ )
+
+
@mock_ssm
def test_delete_parameters():
client = boto3.client("ssm", region_name="us-east-1")
diff --git a/tests/test_swf/responses/test_activity_types.py b/tests/test_swf/responses/test_activity_types.py
index 3fa9ad6b1..d49e5d4cb 100644
--- a/tests/test_swf/responses/test_activity_types.py
+++ b/tests/test_swf/responses/test_activity_types.py
@@ -1,8 +1,11 @@
import boto
from boto.swf.exceptions import SWFResponseError
+import boto3
+from botocore.exceptions import ClientError
import sure # noqa
from moto import mock_swf_deprecated
+from moto import mock_swf
# RegisterActivityType endpoint
@@ -110,6 +113,77 @@ def test_deprecate_non_existent_activity_type():
).should.throw(SWFResponseError)
+# DeprecateActivityType endpoint
+@mock_swf
+def test_undeprecate_activity_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.register_activity_type(
+ domain="test-domain", name="test-activity", version="v1.0"
+ )
+ client.deprecate_activity_type(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ )
+ client.undeprecate_activity_type(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ )
+
+ resp = client.describe_activity_type(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ )
+ resp["typeInfo"]["status"].should.equal("REGISTERED")
+
+
+@mock_swf
+def test_undeprecate_already_undeprecated_activity_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.register_activity_type(
+ domain="test-domain", name="test-activity", version="v1.0"
+ )
+ client.deprecate_activity_type(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ )
+ client.undeprecate_activity_type(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ )
+
+ client.undeprecate_activity_type.when.called_with(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ ).should.throw(ClientError)
+
+
+@mock_swf
+def test_undeprecate_never_deprecated_activity_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.register_activity_type(
+ domain="test-domain", name="test-activity", version="v1.0"
+ )
+
+ client.undeprecate_activity_type.when.called_with(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ ).should.throw(ClientError)
+
+
+@mock_swf
+def test_undeprecate_non_existent_activity_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+
+ client.undeprecate_activity_type.when.called_with(
+ domain="test-domain", activityType={"name": "test-activity", "version": "v1.0"}
+ ).should.throw(ClientError)
+
+
# DescribeActivityType endpoint
@mock_swf_deprecated
def test_describe_activity_type():
diff --git a/tests/test_swf/responses/test_domains.py b/tests/test_swf/responses/test_domains.py
index 199219d27..59ba551a6 100644
--- a/tests/test_swf/responses/test_domains.py
+++ b/tests/test_swf/responses/test_domains.py
@@ -1,8 +1,11 @@
import boto
from boto.swf.exceptions import SWFResponseError
+import boto3
+from botocore.exceptions import ClientError
import sure # noqa
from moto import mock_swf_deprecated
+from moto import mock_swf
# RegisterDomain endpoint
@@ -94,6 +97,56 @@ def test_deprecate_non_existent_domain():
)
+# UndeprecateDomain endpoint
+@mock_swf
+def test_undeprecate_domain():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.deprecate_domain(name="test-domain")
+ client.undeprecate_domain(name="test-domain")
+
+ resp = client.describe_domain(name="test-domain")
+
+ resp["domainInfo"]["status"].should.equal("REGISTERED")
+
+
+@mock_swf
+def test_undeprecate_already_undeprecated_domain():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.deprecate_domain(name="test-domain")
+ client.undeprecate_domain(name="test-domain")
+
+ client.undeprecate_domain.when.called_with(name="test-domain").should.throw(
+ ClientError
+ )
+
+
+@mock_swf
+def test_undeprecate_never_deprecated_domain():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+
+ client.undeprecate_domain.when.called_with(name="test-domain").should.throw(
+ ClientError
+ )
+
+
+@mock_swf
+def test_undeprecate_non_existent_domain():
+ client = boto3.client("swf", region_name="us-east-1")
+
+ client.undeprecate_domain.when.called_with(name="non-existent").should.throw(
+ ClientError
+ )
+
+
# DescribeDomain endpoint
@mock_swf_deprecated
def test_describe_domain():
diff --git a/tests/test_swf/responses/test_workflow_types.py b/tests/test_swf/responses/test_workflow_types.py
index 72aa814d2..e1990596b 100644
--- a/tests/test_swf/responses/test_workflow_types.py
+++ b/tests/test_swf/responses/test_workflow_types.py
@@ -5,6 +5,7 @@ import boto3
from moto import mock_swf_deprecated
from moto import mock_swf
from boto.swf.exceptions import SWFResponseError
+from botocore.exceptions import ClientError
# RegisterWorkflowType endpoint
@@ -112,6 +113,77 @@ def test_deprecate_non_existent_workflow_type():
).should.throw(SWFResponseError)
+# UndeprecateWorkflowType endpoint
+@mock_swf
+def test_undeprecate_workflow_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.register_workflow_type(
+ domain="test-domain", name="test-workflow", version="v1.0"
+ )
+ client.deprecate_workflow_type(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ )
+ client.undeprecate_workflow_type(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ )
+
+ resp = client.describe_workflow_type(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ )
+ resp["typeInfo"]["status"].should.equal("REGISTERED")
+
+
+@mock_swf
+def test_undeprecate_already_undeprecated_workflow_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.register_workflow_type(
+ domain="test-domain", name="test-workflow", version="v1.0"
+ )
+ client.deprecate_workflow_type(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ )
+ client.undeprecate_workflow_type(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ )
+
+ client.undeprecate_workflow_type.when.called_with(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ ).should.throw(ClientError)
+
+
+@mock_swf
+def test_undeprecate_never_deprecated_workflow_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+ client.register_workflow_type(
+ domain="test-domain", name="test-workflow", version="v1.0"
+ )
+
+ client.undeprecate_workflow_type.when.called_with(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ ).should.throw(ClientError)
+
+
+@mock_swf
+def test_undeprecate_non_existent_workflow_type():
+ client = boto3.client("swf", region_name="us-east-1")
+ client.register_domain(
+ name="test-domain", workflowExecutionRetentionPeriodInDays="60"
+ )
+
+ client.undeprecate_workflow_type.when.called_with(
+ domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
+ ).should.throw(ClientError)
+
+
# DescribeWorkflowType endpoint
@mock_swf_deprecated
def test_describe_workflow_type():