From bcd0e5490743248cdb96083cec3364bd9f1dd951 Mon Sep 17 00:00:00 2001 From: cm-iwata <38879253+cm-iwata@users.noreply.github.com> Date: Fri, 17 Jun 2022 23:07:37 +0900 Subject: [PATCH] Greengrass Implement subscription_definition APIs (#5240) --- moto/greengrass/models.py | 195 ++++++ moto/greengrass/responses.py | 110 ++++ moto/greengrass/urls.py | 4 + .../test_greengrass_subscriptions.py | 596 ++++++++++++++++++ 4 files changed, 905 insertions(+) create mode 100644 tests/test_greengrass/test_greengrass_subscriptions.py diff --git a/moto/greengrass/models.py b/moto/greengrass/models.py index dd68c878c94..def34fe76ee 100644 --- a/moto/greengrass/models.py +++ b/moto/greengrass/models.py @@ -1,6 +1,7 @@ import uuid from collections import OrderedDict from datetime import datetime +import re from moto.core import BaseBackend, BaseModel, get_account_id from moto.core.utils import BackendDict, iso_8601_datetime_with_milliseconds @@ -220,6 +221,55 @@ def to_dict(self): } +class FakeSubscriptionDefinition(BaseModel): + def __init__(self, region_name, name, initial_version): + self.region_name = region_name + self.id = str(uuid.uuid4()) + self.arn = f"arn:aws:greengrass:{self.region_name}:{get_account_id()}:/greengrass/definition/subscriptions/{self.id}" + self.created_at_datetime = datetime.utcnow() + self.update_at_datetime = datetime.utcnow() + self.latest_version = "" + self.latest_version_arn = "" + self.name = name + self.initial_version = initial_version + + def to_dict(self): + return { + "Arn": self.arn, + "CreationTimestamp": iso_8601_datetime_with_milliseconds( + self.created_at_datetime + ), + "Id": self.id, + "LastUpdatedTimestamp": iso_8601_datetime_with_milliseconds( + self.update_at_datetime + ), + "LatestVersion": self.latest_version, + "LatestVersionArn": self.latest_version_arn, + "Name": self.name, + } + + +class FakeSubscriptionDefinitionVersion(BaseModel): + def __init__(self, region_name, subscription_definition_id, subscriptions): + self.region_name = region_name + self.subscription_definition_id = subscription_definition_id + self.subscriptions = subscriptions + self.version = str(uuid.uuid4()) + self.arn = f"arn:aws:greengrass:{self.region_name}:{get_account_id()}:/greengrass/definition/subscriptions/{self.subscription_definition_id}/versions/{self.version}" + self.created_at_datetime = datetime.utcnow() + + def to_dict(self): + return { + "Arn": self.arn, + "CreationTimestamp": iso_8601_datetime_with_milliseconds( + self.created_at_datetime + ), + "Definition": {"Subscriptions": self.subscriptions}, + "Id": self.subscription_definition_id, + "Version": self.version, + } + + class GreengrassBackend(BaseBackend): def __init__(self, region_name, account_id): super().__init__(region_name, account_id) @@ -595,5 +645,150 @@ def get_function_definition_version( function_definition_version_id ] + @staticmethod + def _is_valid_subscription_target_or_source(target_or_source): + + if target_or_source in ["cloud", "GGShadowService"]: + return True + + if re.match( + r"^arn:aws:iot:[a-zA-Z0-9-]+:[0-9]{12}:thing/[a-zA-Z0-9-]+$", + target_or_source, + ): + return True + + if re.match( + r"^arn:aws:lambda:[a-zA-Z0-9-]+:[0-9]{12}:function:[a-zA-Z0-9-_]+:[a-zA-Z0-9-_]+$", + target_or_source, + ): + return True + + return False + + @staticmethod + def _validate_subscription_target_or_source(subscriptions): + + target_errors = [] + source_errors = [] + + for subscription in subscriptions: + subscription_id = subscription["Id"] + source = subscription["Source"] + target = subscription["Target"] + + if not GreengrassBackend._is_valid_subscription_target_or_source(source): + target_errors.append( + f"Subscription source is invalid. ID is '{subscription_id}' and Source is '{source}'" + ) + + if not GreengrassBackend._is_valid_subscription_target_or_source(target): + target_errors.append( + f"Subscription target is invalid. ID is '{subscription_id}' and Target is '{target}'" + ) + + if source_errors: + error_msg = ", ".join(source_errors) + raise GreengrassClientError( + "400", + f"The subscriptions definition is invalid or corrupted. (ErrorDetails: [{error_msg}])", + ) + + if target_errors: + error_msg = ", ".join(target_errors) + raise GreengrassClientError( + "400", + f"The subscriptions definition is invalid or corrupted. (ErrorDetails: [{error_msg}])", + ) + + def create_subscription_definition(self, name, initial_version): + + GreengrassBackend._validate_subscription_target_or_source( + initial_version["Subscriptions"] + ) + + sub_def = FakeSubscriptionDefinition(self.region_name, name, initial_version) + self.subscription_definitions[sub_def.id] = sub_def + init_ver = sub_def.initial_version + subscriptions = init_ver.get("Subscriptions", {}) + sub_def_ver = self.create_subscription_definition_version( + sub_def.id, subscriptions + ) + + sub_def.latest_version = sub_def_ver.version + sub_def.latest_version_arn = sub_def_ver.arn + return sub_def + + def list_subscription_definitions(self): + return self.subscription_definitions.values() + + def get_subscription_definition(self, subscription_definition_id): + + if subscription_definition_id not in self.subscription_definitions: + raise IdNotFoundException( + "That Subscription List Definition does not exist." + ) + return self.subscription_definitions[subscription_definition_id] + + def delete_subscription_definition(self, subscription_definition_id): + if subscription_definition_id not in self.subscription_definitions: + raise IdNotFoundException("That subscriptions definition does not exist.") + del self.subscription_definitions[subscription_definition_id] + del self.subscription_definition_versions[subscription_definition_id] + + def update_subscription_definition(self, subscription_definition_id, name): + + if name == "": + raise InvalidContainerDefinitionException( + "Input does not contain any attributes to be updated" + ) + if subscription_definition_id not in self.subscription_definitions: + raise IdNotFoundException("That subscriptions definition does not exist.") + self.subscription_definitions[subscription_definition_id].name = name + + def create_subscription_definition_version( + self, subscription_definition_id, subscriptions + ): + + GreengrassBackend._validate_subscription_target_or_source(subscriptions) + + if subscription_definition_id not in self.subscription_definitions: + raise IdNotFoundException("That subscriptions does not exist.") + + sub_def_ver = FakeSubscriptionDefinitionVersion( + self.region_name, subscription_definition_id, subscriptions + ) + + sub_vers = self.subscription_definition_versions.get( + subscription_definition_id, {} + ) + sub_vers[sub_def_ver.version] = sub_def_ver + self.subscription_definition_versions[subscription_definition_id] = sub_vers + + return sub_def_ver + + def list_subscription_definition_versions(self, subscription_definition_id): + if subscription_definition_id not in self.subscription_definition_versions: + raise IdNotFoundException("That subscriptions definition does not exist.") + return self.subscription_definition_versions[subscription_definition_id] + + def get_subscription_definition_version( + self, subscription_definition_id, subscription_definition_version_id + ): + + if subscription_definition_id not in self.subscription_definitions: + raise IdNotFoundException("That subscriptions definition does not exist.") + + if ( + subscription_definition_version_id + not in self.subscription_definition_versions[subscription_definition_id] + ): + raise VersionNotFoundException( + f"Version {subscription_definition_version_id} of Subscription List Definition {subscription_definition_id} does not exist." + ) + + return self.subscription_definition_versions[subscription_definition_id][ + subscription_definition_version_id + ] + greengrass_backends = BackendDict(GreengrassBackend, "greengrass") diff --git a/moto/greengrass/responses.py b/moto/greengrass/responses.py index ba9a58dacc4..4ffb3b86414 100644 --- a/moto/greengrass/responses.py +++ b/moto/greengrass/responses.py @@ -448,3 +448,113 @@ def get_function_definition_version(self): function_definition_version_id=function_definition_version_id, ) return 200, {"status": 200}, json.dumps(res.to_dict()) + + def subscription_definitions(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + + if self.method == "POST": + return self.create_subscription_definition() + + if self.method == "GET": + return self.list_subscription_definitions() + + def create_subscription_definition(self): + + initial_version = self._get_param("InitialVersion") + name = self._get_param("Name") + res = self.greengrass_backend.create_subscription_definition( + name=name, initial_version=initial_version + ) + return 201, {"status": 201}, json.dumps(res.to_dict()) + + def list_subscription_definitions(self): + + res = self.greengrass_backend.list_subscription_definitions() + return ( + 200, + {"status": 200}, + json.dumps( + { + "Definitions": [ + subscription_definition.to_dict() + for subscription_definition in res + ] + } + ), + ) + + def subscription_definition(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + + if self.method == "GET": + return self.get_subscription_definition() + + if self.method == "DELETE": + return self.delete_subscription_definition() + + if self.method == "PUT": + return self.update_subscription_definition() + + def get_subscription_definition(self): + subscription_definition_id = self.path.split("/")[-1] + res = self.greengrass_backend.get_subscription_definition( + subscription_definition_id=subscription_definition_id + ) + return 200, {"status": 200}, json.dumps(res.to_dict()) + + def delete_subscription_definition(self): + subscription_definition_id = self.path.split("/")[-1] + self.greengrass_backend.delete_subscription_definition( + subscription_definition_id=subscription_definition_id + ) + return 200, {"status": 200}, json.dumps({}) + + def update_subscription_definition(self): + subscription_definition_id = self.path.split("/")[-1] + name = self._get_param("Name") + self.greengrass_backend.update_subscription_definition( + subscription_definition_id=subscription_definition_id, name=name + ) + return 200, {"status": 200}, json.dumps({}) + + def subscription_definition_versions(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + + if self.method == "POST": + return self.create_subscription_definition_version() + + if self.method == "GET": + return self.list_subscription_definition_versions() + + def create_subscription_definition_version(self): + + subscription_definition_id = self.path.split("/")[-2] + subscriptions = self._get_param("Subscriptions") + res = self.greengrass_backend.create_subscription_definition_version( + subscription_definition_id=subscription_definition_id, + subscriptions=subscriptions, + ) + return 201, {"status": 201}, json.dumps(res.to_dict()) + + def list_subscription_definition_versions(self): + subscription_definition_id = self.path.split("/")[-2] + res = self.greengrass_backend.list_subscription_definition_versions( + subscription_definition_id=subscription_definition_id + ) + versions = [i.to_dict() for i in res.values()] + return 200, {"status": 200}, json.dumps({"Versions": versions}) + + def subscription_definition_version(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + + if self.method == "GET": + return self.get_subscription_definition_version() + + def get_subscription_definition_version(self): + subscription_definition_id = self.path.split("/")[-3] + subscription_definition_version_id = self.path.split("/")[-1] + res = self.greengrass_backend.get_subscription_definition_version( + subscription_definition_id=subscription_definition_id, + subscription_definition_version_id=subscription_definition_version_id, + ) + return 200, {"status": 200}, json.dumps(res.to_dict()) diff --git a/moto/greengrass/urls.py b/moto/greengrass/urls.py index dfde7cf6c39..54cbb3cebbf 100644 --- a/moto/greengrass/urls.py +++ b/moto/greengrass/urls.py @@ -24,5 +24,9 @@ "{0}/greengrass/definition/resources$": response.resource_definitions, "{0}/greengrass/definition/resources/(?P[^/]+)/?$": response.resource_definition, "{0}/greengrass/definition/resources/(?P[^/]+)/versions$": response.resource_definition_versions, + "{0}/greengrass/definition/subscriptions$": response.subscription_definitions, + "{0}/greengrass/definition/subscriptions/(?P[^/]+)/?$": response.subscription_definition, + "{0}/greengrass/definition/subscriptions/(?P[^/]+)/versions$": response.subscription_definition_versions, + "{0}/greengrass/definition/subscriptions/(?P[^/]+)/versions/(?P[^/]+)/?$": response.subscription_definition_version, "{0}/greengrass/definition/resources/(?P[^/]+)/versions/(?P[^/]+)/?$": response.resource_definition_version, } diff --git a/tests/test_greengrass/test_greengrass_subscriptions.py b/tests/test_greengrass/test_greengrass_subscriptions.py new file mode 100644 index 00000000000..ec75ba46be3 --- /dev/null +++ b/tests/test_greengrass/test_greengrass_subscriptions.py @@ -0,0 +1,596 @@ +import boto3 +from botocore.client import ClientError +import freezegun +import pytest + +from moto import mock_greengrass +from moto.core import get_account_id +from moto.settings import TEST_SERVER_MODE + +ACCOUNT_ID = get_account_id() + + +@pytest.mark.parametrize( + "target", + [ + "cloud", + "GGShadowService", + "arn:aws:iot:ap-northeast-1:123456789012:thing/2ec8d399c34e1e3fe8da266559adcb7e47c989aab353bc0fc7d0f4bad66030ff", + "arn:aws:lambda:ap-northeast-1:123456789012:function:test-func:v1", + ], +) +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_create_subscription_definition(target): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": target, + } + ] + } + subscription_name = "TestSubscription" + res = client.create_subscription_definition( + InitialVersion=init_ver, Name=subscription_name + ) + res.should.have.key("Arn") + res.should.have.key("Id") + res.should.have.key("LatestVersion") + res.should.have.key("LatestVersionArn") + res.should.have.key("Name").equals(subscription_name) + res["ResponseMetadata"]["HTTPStatusCode"].should.equal(201) + + if not TEST_SERVER_MODE: + res.should.have.key("CreationTimestamp").equals("2022-06-01T12:00:00.000Z") + res.should.have.key("LastUpdatedTimestamp").equals("2022-06-01T12:00:00.000Z") + + +@mock_greengrass +def test_create_subscription_definition_with_invalid_target(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "foo", + } + ] + } + with pytest.raises(ClientError) as ex: + client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + + ex.value.response["Error"]["Message"].should.equal( + "The subscriptions definition is invalid or corrupted. (ErrorDetails: [Subscription target is invalid. ID is '123456' and Target is 'foo'])" + ) + ex.value.response["Error"]["Code"].should.equal("400") + + +@mock_greengrass +def test_create_subscription_definition_with_invalid_source(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "foo", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + } + ] + } + with pytest.raises(ClientError) as ex: + client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + + ex.value.response["Error"]["Message"].should.equal( + "The subscriptions definition is invalid or corrupted. (ErrorDetails: [Subscription source is invalid. ID is '123456' and Source is 'foo'])" + ) + ex.value.response["Error"]["Code"].should.equal("400") + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_list_subscription_definitions(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + subscription_name = "TestSubscription" + client.create_subscription_definition( + InitialVersion=init_ver, Name=subscription_name + ) + + res = client.list_subscription_definitions() + res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + subscription_def = res["Definitions"][0] + subscription_def.should.have.key("Name").equals(subscription_name) + subscription_def.should.have.key("Arn") + subscription_def.should.have.key("Id") + subscription_def.should.have.key("LatestVersion") + subscription_def.should.have.key("LatestVersionArn") + if not TEST_SERVER_MODE: + subscription_def.should.have.key("CreationTimestamp").equal( + "2022-06-01T12:00:00.000Z" + ) + subscription_def.should.have.key("LastUpdatedTimestamp").equals( + "2022-06-01T12:00:00.000Z" + ) + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_get_subscription_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + subscription_name = "TestSubscription" + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name=subscription_name + ) + + subscription_def_id = create_res["Id"] + arn = create_res["Arn"] + latest_version = create_res["LatestVersion"] + latest_version_arn = create_res["LatestVersionArn"] + + get_res = client.get_subscription_definition( + SubscriptionDefinitionId=subscription_def_id + ) + + get_res.should.have.key("Name").equals(subscription_name) + get_res.should.have.key("Arn").equals(arn) + get_res.should.have.key("Id").equals(subscription_def_id) + get_res.should.have.key("LatestVersion").equals(latest_version) + get_res.should.have.key("LatestVersionArn").equals(latest_version_arn) + get_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + if not TEST_SERVER_MODE: + get_res.should.have.key("CreationTimestamp").equal("2022-06-01T12:00:00.000Z") + get_res.should.have.key("LastUpdatedTimestamp").equals( + "2022-06-01T12:00:00.000Z" + ) + + +@mock_greengrass +def test_get_subscription_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + with pytest.raises(ClientError) as ex: + client.get_subscription_definition( + SubscriptionDefinitionId="b552443b-1888-469b-81f8-0ebc5ca92949" + ) + + ex.value.response["Error"]["Message"].should.equal( + "That Subscription List Definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_delete_subscription_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + + subscription_def_id = create_res["Id"] + del_res = client.delete_subscription_definition( + SubscriptionDefinitionId=subscription_def_id + ) + del_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + +@mock_greengrass +def test_update_subscription_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + + subscription_def_id = create_res["Id"] + updated_subscription_name = "UpdatedSubscription" + update_res = client.update_subscription_definition( + SubscriptionDefinitionId=subscription_def_id, Name=updated_subscription_name + ) + update_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + get_res = client.get_subscription_definition( + SubscriptionDefinitionId=subscription_def_id + ) + get_res.should.have.key("Name").equals(updated_subscription_name) + + +@mock_greengrass +def test_update_subscription_definition_with_empty_name(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + subscription_def_id = create_res["Id"] + + with pytest.raises(ClientError) as ex: + client.update_subscription_definition( + SubscriptionDefinitionId=subscription_def_id, Name="" + ) + ex.value.response["Error"]["Message"].should.equal( + "Input does not contain any attributes to be updated" + ) + ex.value.response["Error"]["Code"].should.equal( + "InvalidContainerDefinitionException" + ) + + +@mock_greengrass +def test_update_subscription_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.update_subscription_definition( + SubscriptionDefinitionId="6fbffc21-989e-4d29-a793-a42f450a78c6", Name="123" + ) + ex.value.response["Error"]["Message"].should.equal( + "That subscriptions definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_delete_subscription_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.delete_subscription_definition( + SubscriptionDefinitionId="6fbffc21-989e-4d29-a793-a42f450a78c6" + ) + ex.value.response["Error"]["Message"].should.equal( + "That subscriptions definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_create_subscription_definition_version(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + v1_subscriptions = [ + { + "Id": "123456", + "Source": "cloud", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + } + ] + + initial_version = {"Subscriptions": v1_subscriptions} + subscription_def_res = client.create_subscription_definition( + InitialVersion=initial_version, Name="TestSubscription" + ) + subscription_def_id = subscription_def_res["Id"] + + v2_subscriptions = [ + { + "Id": "123456", + "Source": "cloud", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:2", + } + ] + + subscription_def_ver_res = client.create_subscription_definition_version( + SubscriptionDefinitionId=subscription_def_id, Subscriptions=v2_subscriptions + ) + subscription_def_ver_res.should.have.key("Arn") + subscription_def_ver_res.should.have.key("CreationTimestamp") + subscription_def_ver_res.should.have.key("Id").equals(subscription_def_id) + subscription_def_ver_res.should.have.key("Version") + + if not TEST_SERVER_MODE: + subscription_def_ver_res["CreationTimestamp"].should.equal( + "2022-06-01T12:00:00.000Z" + ) + + +@mock_greengrass +def test_create_subscription_definition_version_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + subscriptions = [ + { + "Id": "123456", + "Source": "cloud", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:2", + } + ] + + with pytest.raises(ClientError) as ex: + client.create_subscription_definition_version( + SubscriptionDefinitionId="7b0bdeae-54c7-47cf-9f93-561e672efd9c", + Subscriptions=subscriptions, + ) + ex.value.response["Error"]["Message"].should.equal( + "That subscriptions does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_create_subscription_definition_version_with_invalid_target(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + v1_subscriptions = [ + { + "Id": "123456", + "Source": "cloud", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + } + ] + + initial_version = {"Subscriptions": v1_subscriptions} + subscription_def_res = client.create_subscription_definition( + InitialVersion=initial_version, Name="TestSubscription" + ) + subscription_def_id = subscription_def_res["Id"] + + v2_subscriptions = [ + { + "Id": "999999", + "Source": "cloud", + "Subject": "foo/bar", + "Target": "foo", + } + ] + + with pytest.raises(ClientError) as ex: + client.create_subscription_definition_version( + SubscriptionDefinitionId=subscription_def_id, Subscriptions=v2_subscriptions + ) + + ex.value.response["Error"]["Message"].should.equal( + "The subscriptions definition is invalid or corrupted. (ErrorDetails: [Subscription target is invalid. ID is '999999' and Target is 'foo'])" + ) + ex.value.response["Error"]["Code"].should.equal("400") + + +@mock_greengrass +def test_create_subscription_definition_version_with_invalid_source(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + v1_subscriptions = [ + { + "Id": "123456", + "Source": "cloud", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + } + ] + + initial_version = {"Subscriptions": v1_subscriptions} + subscription_def_res = client.create_subscription_definition( + InitialVersion=initial_version, Name="TestSubscription" + ) + subscription_def_id = subscription_def_res["Id"] + + v2_subscriptions = [ + { + "Id": "999999", + "Source": "foo", + "Subject": "foo/bar", + "Target": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + } + ] + + with pytest.raises(ClientError) as ex: + client.create_subscription_definition_version( + SubscriptionDefinitionId=subscription_def_id, Subscriptions=v2_subscriptions + ) + + ex.value.response["Error"]["Message"].should.equal( + "The subscriptions definition is invalid or corrupted. (ErrorDetails: [Subscription source is invalid. ID is '999999' and Source is 'foo'])" + ) + ex.value.response["Error"]["Code"].should.equal("400") + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_list_subscription_definition_versions(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + subscription_def_id = create_res["Id"] + subscription_def_ver_res = client.list_subscription_definition_versions( + SubscriptionDefinitionId=subscription_def_id + ) + + subscription_def_ver_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + subscription_def_ver_res.should.have.key("Versions") + subscription_def_ver = subscription_def_ver_res["Versions"][0] + subscription_def_ver.should.have.key("Arn") + subscription_def_ver.should.have.key("CreationTimestamp") + subscription_def_ver.should.have.key("Id").equals(subscription_def_id) + subscription_def_ver.should.have.key("Version") + + if not TEST_SERVER_MODE: + subscription_def_ver["CreationTimestamp"].should.equal( + "2022-06-01T12:00:00.000Z" + ) + + +@mock_greengrass +def test_list_subscription_definition_versions_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.list_subscription_definition_versions( + SubscriptionDefinitionId="7b0bdeae-54c7-47cf-9f93-561e672efd9c" + ) + ex.value.response["Error"]["Message"].should.equal( + "That subscriptions definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_get_subscription_definition_version(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + + subscription_def_id = create_res["Id"] + subscription_def_ver_id = create_res["LatestVersion"] + + func_def_ver_res = client.get_subscription_definition_version( + SubscriptionDefinitionId=subscription_def_id, + SubscriptionDefinitionVersionId=subscription_def_ver_id, + ) + + func_def_ver_res.should.have.key("Arn") + func_def_ver_res.should.have.key("CreationTimestamp") + func_def_ver_res.should.have.key("Definition").should.equal(init_ver) + func_def_ver_res.should.have.key("Id").equals(subscription_def_id) + func_def_ver_res.should.have.key("Version") + + if not TEST_SERVER_MODE: + func_def_ver_res["CreationTimestamp"].should.equal("2022-06-01T12:00:00.000Z") + + +@mock_greengrass +def test_get_subscription_definition_version_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.get_subscription_definition_version( + SubscriptionDefinitionId="7b0bdeae-54c7-47cf-9f93-561e672efd9c", + SubscriptionDefinitionVersionId="7b0bdeae-54c7-47cf-9f93-561e672efd9c", + ) + ex.value.response["Error"]["Message"].should.equal( + "That subscriptions definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_get_subscription_definition_version_with_invalid_version_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Subscriptions": [ + { + "Id": "123456", + "Source": "arn:aws:lambda:ap-northeast-1:123456789012:function:test_func:1", + "Subject": "foo/bar", + "Target": "cloud", + } + ] + } + create_res = client.create_subscription_definition( + InitialVersion=init_ver, Name="TestSubscription" + ) + + subscription_def_id = create_res["Id"] + invalid_subscription_def_ver_id = "7b0bdeae-54c7-47cf-9f93-561e672efd9c" + + with pytest.raises(ClientError) as ex: + client.get_subscription_definition_version( + SubscriptionDefinitionId=subscription_def_id, + SubscriptionDefinitionVersionId=invalid_subscription_def_ver_id, + ) + ex.value.response["Error"]["Message"].should.equal( + f"Version {invalid_subscription_def_ver_id} of Subscription List Definition {subscription_def_id} does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("VersionNotFoundException")