From d4355a93f80c690a22399bfc6ec85fcbe32f318e Mon Sep 17 00:00:00 2001 From: cm-iwata <38879253+cm-iwata@users.noreply.github.com> Date: Thu, 9 Jun 2022 18:44:33 +0900 Subject: [PATCH] Greengrass Implement device_definition APIs (#5206) --- moto/greengrass/models.py | 25 +++ moto/greengrass/responses.py | 53 +++++ moto/greengrass/urls.py | 1 + .../test_greengrass/test_greengrass_device.py | 200 ++++++++++++++++++ 4 files changed, 279 insertions(+) diff --git a/moto/greengrass/models.py b/moto/greengrass/models.py index e250819cc..3ebbcb396 100644 --- a/moto/greengrass/models.py +++ b/moto/greengrass/models.py @@ -215,6 +215,9 @@ class GreengrassBackend(BaseBackend): return device_def + def list_device_definitions(self): + return self.device_definitions.values() + def create_device_definition_version(self, device_definition_id, devices): if device_definition_id not in self.device_definitions: @@ -237,5 +240,27 @@ class GreengrassBackend(BaseBackend): return device_ver + def get_device_definition(self, device_definition_id): + + if device_definition_id not in self.device_definitions: + raise IdNotFoundException("That Device List Definition does not exist.") + return self.device_definitions[device_definition_id] + + def delete_device_definition(self, device_definition_id): + if device_definition_id not in self.device_definitions: + raise IdNotFoundException("That devices definition does not exist.") + del self.device_definitions[device_definition_id] + del self.device_definition_versions[device_definition_id] + + def update_device_definition(self, device_definition_id, name): + + if name == "": + raise InvalidContainerDefinitionException( + "Input does not contain any attributes to be updated" + ) + if device_definition_id not in self.device_definitions: + raise IdNotFoundException("That devices definition does not exist.") + self.device_definitions[device_definition_id].name = name + greengrass_backends = BackendDict(GreengrassBackend, "greengrass") diff --git a/moto/greengrass/responses.py b/moto/greengrass/responses.py index 2d740cb3c..994f2df2e 100644 --- a/moto/greengrass/responses.py +++ b/moto/greengrass/responses.py @@ -120,6 +120,9 @@ class GreengrassResponse(BaseResponse): if self.method == "POST": return self.create_device_definition() + if self.method == "GET": + return self.list_device_definition() + def create_device_definition(self): name = self._get_param("Name") @@ -129,6 +132,20 @@ class GreengrassResponse(BaseResponse): ) return 201, {"status": 201}, json.dumps(res.to_dict()) + def list_device_definition(self): + res = self.greengrass_backend.list_device_definitions() + return ( + 200, + {"status": 200}, + json.dumps( + { + "Definitions": [ + device_definition.to_dict() for device_definition in res + ] + } + ), + ) + def device_definition_versions(self, request, full_url, headers): self.setup_class(request, full_url, headers) @@ -144,3 +161,39 @@ class GreengrassResponse(BaseResponse): device_definition_id=device_definition_id, devices=devices ) return 201, {"status": 201}, json.dumps(res.to_dict()) + + def device_definition(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + + if self.method == "GET": + return self.get_device_definition() + + if self.method == "DELETE": + return self.delete_device_definition() + + if self.method == "PUT": + return self.update_device_definition() + + def get_device_definition(self): + device_definition_id = self.path.split("/")[-1] + res = self.greengrass_backend.get_device_definition( + device_definition_id=device_definition_id + ) + return 200, {"status": 200}, json.dumps(res.to_dict()) + + def delete_device_definition(self): + + device_definition_id = self.path.split("/")[-1] + self.greengrass_backend.delete_device_definition( + device_definition_id=device_definition_id + ) + return 200, {"status": 200}, json.dumps({}) + + def update_device_definition(self): + + device_definition_id = self.path.split("/")[-1] + name = self._get_param("Name") + self.greengrass_backend.update_device_definition( + device_definition_id=device_definition_id, name=name + ) + return 200, {"status": 200}, json.dumps({}) diff --git a/moto/greengrass/urls.py b/moto/greengrass/urls.py index ec4194498..c98ad6c4f 100644 --- a/moto/greengrass/urls.py +++ b/moto/greengrass/urls.py @@ -14,5 +14,6 @@ url_paths = { "{0}/greengrass/definition/cores/(?P[^/]+)/versions$": response.core_definition_versions, "{0}/greengrass/definition/cores/(?P[^/]+)/versions/(?P[^/]+)/?$": response.core_definition_version, "{0}/greengrass/definition/devices$": response.device_definitions, + "{0}/greengrass/definition/devices/(?P[^/]+)/?$": response.device_definition, "{0}/greengrass/definition/devices/(?P[^/]+)/versions$": response.device_definition_versions, } diff --git a/tests/test_greengrass/test_greengrass_device.py b/tests/test_greengrass/test_greengrass_device.py index 44bd65fb6..0711707ec 100644 --- a/tests/test_greengrass/test_greengrass_device.py +++ b/tests/test_greengrass/test_greengrass_device.py @@ -40,6 +40,97 @@ def test_create_device_definition(): res.should.have.key("LastUpdatedTimestamp").equals("2022-06-01T12:00:00.000Z") +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_list_device_definitions(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Devices": [ + { + "CertificateArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:cert/36ed61be9c6271ae8da174e29d0e033c06af149d7b21672f3800fe322044554d", + "Id": "123", + "SyncShadow": True, + "ThingArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:thing/CoreThing", + } + ] + } + + device_name = "TestDevice" + client.create_device_definition(InitialVersion=init_ver, Name=device_name) + + res = client.list_device_definitions() + res.should.have.key("Definitions") + device_definition = res["Definitions"][0] + + device_definition.should.have.key("Name").equals(device_name) + device_definition.should.have.key("Arn") + device_definition.should.have.key("Id") + device_definition.should.have.key("LatestVersion") + device_definition.should.have.key("LatestVersionArn") + if not TEST_SERVER_MODE: + device_definition.should.have.key("CreationTimestamp").equal( + "2022-06-01T12:00:00.000Z" + ) + device_definition.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_device_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Devices": [ + { + "CertificateArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:cert/36ed61be9c6271ae8da174e29d0e033c06af149d7b21672f3800fe322044554d", + "Id": "123", + "SyncShadow": True, + "ThingArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:thing/CoreThing", + } + ] + } + + device_name = "TestDevice" + create_res = client.create_device_definition( + InitialVersion=init_ver, Name=device_name + ) + device_def_id = create_res["Id"] + arn = create_res["Arn"] + latest_version = create_res["LatestVersion"] + latest_version_arn = create_res["LatestVersionArn"] + + get_res = client.get_device_definition(DeviceDefinitionId=device_def_id) + + get_res.should.have.key("Name").equals(device_name) + get_res.should.have.key("Arn").equals(arn) + get_res.should.have.key("Id").equals(device_def_id) + get_res.should.have.key("LatestVersion").equals(latest_version) + get_res.should.have.key("LatestVersionArn").equals(latest_version_arn) + + 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_device_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + with pytest.raises(ClientError) as ex: + client.get_device_definition( + DeviceDefinitionId="b552443b-1888-469b-81f8-0ebc5ca92949" + ) + ex.value.response["Error"]["Message"].should.equal( + "That Device List 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_device_definition_version(): @@ -107,3 +198,112 @@ def test_create_device_definition_version_with_invalid_id(): "That devices definition does not exist." ) ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_delete_device_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + devices = [ + { + "CertificateArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:cert/36ed61be9c6271ae8da174e29d0e033c06af149d7b21672f3800fe322044554d", + "Id": "123", + "SyncShadow": True, + "ThingArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:thing/v1Thing", + } + ] + + create_res = client.create_device_definition( + InitialVersion={"Devices": devices}, Name="TestDevice" + ) + + device_def_id = create_res["Id"] + del_res = client.delete_device_definition(DeviceDefinitionId=device_def_id) + del_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + +@mock_greengrass +def test_delete_device_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.delete_device_definition( + DeviceDefinitionId="6fbffc21-989e-4d29-a793-a42f450a78c6" + ) + ex.value.response["Error"]["Message"].should.equal( + "That devices definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_update_device_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + devices = [ + { + "CertificateArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:cert/36ed61be9c6271ae8da174e29d0e033c06af149d7b21672f3800fe322044554d", + "Id": "123", + "SyncShadow": True, + "ThingArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:thing/v1Thing", + } + ] + + initial_version = {"Devices": devices} + create_res = client.create_device_definition( + InitialVersion=initial_version, Name="TestDevice" + ) + device_def_id = create_res["Id"] + updated_device_name = "UpdatedDevice" + update_res = client.update_device_definition( + DeviceDefinitionId=device_def_id, Name=updated_device_name + ) + update_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + get_res = client.get_device_definition(DeviceDefinitionId=device_def_id) + get_res.should.have.key("Name").equals(updated_device_name) + + +@mock_greengrass +def test_update_device_definition_with_empty_name(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + devices = [ + { + "CertificateArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:cert/36ed61be9c6271ae8da174e29d0e033c06af149d7b21672f3800fe322044554d", + "Id": "123", + "SyncShadow": True, + "ThingArn": f"arn:aws:iot:ap-northeast-1:{ACCOUNT_ID}:thing/v1Thing", + } + ] + + initial_version = {"Devices": devices} + create_res = client.create_device_definition( + InitialVersion=initial_version, Name="TestDevice" + ) + device_def_id = create_res["Id"] + + with pytest.raises(ClientError) as ex: + client.update_device_definition(DeviceDefinitionId=device_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_device_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.update_device_definition( + DeviceDefinitionId="6fbffc21-989e-4d29-a793-a42f450a78c6", Name="123" + ) + ex.value.response["Error"]["Message"].should.equal( + "That devices definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException")