diff --git a/moto/iot/models.py b/moto/iot/models.py index 713d02d27..56a852dbe 100644 --- a/moto/iot/models.py +++ b/moto/iot/models.py @@ -1301,14 +1301,17 @@ class IoTBackend(BaseBackend): if attribute_payload is not None and "attributes" in attribute_payload: do_merge = attribute_payload.get("merge", False) attributes = attribute_payload["attributes"] - if not do_merge: - thing_group.thing_group_properties["attributePayload"][ - "attributes" - ] = attributes - else: - thing_group.thing_group_properties["attributePayload"][ - "attributes" - ].update(attributes) + if attributes: + # might not exist yet, for example when the thing group was created without attributes + current_attribute_payload = ( + thing_group.thing_group_properties.setdefault( + "attributePayload", {"attributes": {}} + ) + ) + if not do_merge: + current_attribute_payload["attributes"] = attributes + else: + current_attribute_payload["attributes"].update(attributes) elif attribute_payload is not None and "attributes" not in attribute_payload: thing_group.attributes = {} if "thingGroupDescription" in thing_group_properties: diff --git a/tests/test_iot/test_iot_thing_groups.py b/tests/test_iot/test_iot_thing_groups.py index d5b08daaa..fca1d23d6 100644 --- a/tests/test_iot/test_iot_thing_groups.py +++ b/tests/test_iot/test_iot_thing_groups.py @@ -615,3 +615,73 @@ def test_thing_group_updates_description(): thing_group.should.have.key("thingGroupProperties").which.should.have.key( "thingGroupDescription" ).which.should.equal(new_description) + + +@mock_iot +def test_thing_group_update_with_no_previous_attributes_no_merge(): + client = boto3.client("iot", region_name="ap-northeast-1") + name = "my-group-name" + client.create_thing_group(thingGroupName=name) + + client.update_thing_group( + thingGroupName=name, + thingGroupProperties={ + "attributePayload": { + "attributes": { + "key1": "val01", + }, + "merge": False, + } + }, + ) + + updated_thing_group = client.describe_thing_group(thingGroupName=name) + updated_thing_group.should.have.key("thingGroupProperties").which.should.have.key( + "attributePayload" + ).which.should.have.key("attributes").which.should.equal({"key1": "val01"}) + + +@mock_iot +def test_thing_group_update_with_no_previous_attributes_with_merge(): + client = boto3.client("iot", region_name="ap-northeast-1") + name = "my-group-name" + client.create_thing_group(thingGroupName=name) + + client.update_thing_group( + thingGroupName=name, + thingGroupProperties={ + "attributePayload": { + "attributes": { + "key1": "val01", + }, + "merge": True, + } + }, + ) + + updated_thing_group = client.describe_thing_group(thingGroupName=name) + updated_thing_group.should.have.key("thingGroupProperties").which.should.have.key( + "attributePayload" + ).which.should.have.key("attributes").which.should.equal({"key1": "val01"}) + + +@mock_iot +def test_thing_group_updated_with_empty_attributes_no_merge_no_attributes_added(): + client = boto3.client("iot", region_name="ap-northeast-1") + name = "my-group-name" + client.create_thing_group(thingGroupName=name) + + client.update_thing_group( + thingGroupName=name, + thingGroupProperties={ + "attributePayload": { + "attributes": {}, + "merge": False, + } + }, + ) + + updated_thing_group = client.describe_thing_group(thingGroupName=name) + updated_thing_group.should.have.key( + "thingGroupProperties" + ).which.should_not.have.key("attributePayload")