diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index 425815f12..75e49c3ca 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -203,15 +203,8 @@ class DynamoHandler(BaseResponse): actual_attrs = [item["AttributeName"] for item in attr] actual_attrs.sort() if actual_attrs != expected_attrs: - er = "com.amazonaws.dynamodb.v20111205#ValidationException" - return self.error( - er, - "One or more parameter values were invalid: " - "Some index key attributes are not defined in AttributeDefinitions. " - "Keys: " - + str(expected_attrs) - + ", AttributeDefinitions: " - + str(actual_attrs), + return self._throw_attr_error( + actual_attrs, expected_attrs, global_indexes or local_secondary_indexes ) # get the stream specification streams = body.get("StreamSpecification") @@ -231,6 +224,62 @@ class DynamoHandler(BaseResponse): er = "com.amazonaws.dynamodb.v20111205#ResourceInUseException" return self.error(er, "Resource in use") + def _throw_attr_error(self, actual_attrs, expected_attrs, indexes): + def dump_list(list_): + return str(list_).replace("'", "") + + er = "com.amazonaws.dynamodb.v20111205#ValidationException" + err_head = "One or more parameter values were invalid: " + if len(actual_attrs) > len(expected_attrs): + if indexes: + return self.error( + er, + err_head + + "Some AttributeDefinitions are not used. AttributeDefinitions: " + + dump_list(actual_attrs) + + ", keys used: " + + dump_list(expected_attrs), + ) + else: + return self.error( + er, + err_head + + "Number of attributes in KeySchema does not exactly match number of attributes defined in AttributeDefinitions", + ) + elif len(actual_attrs) < len(expected_attrs): + if indexes: + return self.error( + er, + err_head + + "Some index key attributes are not defined in AttributeDefinitions. Keys: " + + dump_list(list(set(expected_attrs) - set(actual_attrs))) + + ", AttributeDefinitions: " + + dump_list(actual_attrs), + ) + else: + return self.error( + er, "Invalid KeySchema: Some index key attribute have no definition" + ) + else: + if indexes: + return self.error( + er, + err_head + + "Some index key attributes are not defined in AttributeDefinitions. Keys: " + + dump_list(list(set(expected_attrs) - set(actual_attrs))) + + ", AttributeDefinitions: " + + dump_list(actual_attrs), + ) + else: + return self.error( + er, + err_head + + "Some index key attributes are not defined in AttributeDefinitions. Keys: " + + dump_list(expected_attrs) + + ", AttributeDefinitions: " + + dump_list(actual_attrs), + ) + def delete_table(self): name = self.body["TableName"] table = self.dynamodb_backend.delete_table(name) diff --git a/tests/test_dynamodb2/test_dynamodb_exceptions.py b/tests/test_dynamodb2/test_dynamodb_exceptions.py index 929601430..427c8d325 100644 --- a/tests/test_dynamodb2/test_dynamodb_exceptions.py +++ b/tests/test_dynamodb2/test_dynamodb_exceptions.py @@ -195,3 +195,136 @@ def test_batch_write_item_non_existing_table(): err = exc.value.response["Error"] assert err["Code"].should.equal("ResourceNotFoundException") assert err["Message"].should.equal("Requested resource not found") + + +@mock_dynamodb2 +def test_create_table_with_redundant_attributes(): + dynamodb = boto3.client("dynamodb", region_name="us-east-1") + + with pytest.raises(ClientError) as exc: + dynamodb.create_table( + TableName="test-table", + AttributeDefinitions=[ + {"AttributeName": "id", "AttributeType": "S"}, + {"AttributeName": "created_at", "AttributeType": "N"}, + ], + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + BillingMode="PAY_PER_REQUEST", + ) + + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "One or more parameter values were invalid: Number of attributes in KeySchema does not exactly match number of attributes defined in AttributeDefinitions" + ) + + with pytest.raises(ClientError) as exc: + dynamodb.create_table( + TableName="test-table", + AttributeDefinitions=[ + {"AttributeName": "id", "AttributeType": "S"}, + {"AttributeName": "user", "AttributeType": "S"}, + {"AttributeName": "created_at", "AttributeType": "N"}, + ], + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + GlobalSecondaryIndexes=[ + { + "IndexName": "gsi_user-items", + "KeySchema": [{"AttributeName": "user", "KeyType": "HASH"}], + "Projection": {"ProjectionType": "ALL"}, + } + ], + BillingMode="PAY_PER_REQUEST", + ) + + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "One or more parameter values were invalid: Some AttributeDefinitions are not used. AttributeDefinitions: [created_at, id, user], keys used: [id, user]" + ) + + +@mock_dynamodb2 +def test_create_table_with_missing_attributes(): + dynamodb = boto3.client("dynamodb", region_name="us-east-1") + + with pytest.raises(ClientError) as exc: + dynamodb.create_table( + TableName="test-table", + AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"},], + KeySchema=[ + {"AttributeName": "id", "KeyType": "HASH"}, + {"AttributeName": "created_at", "KeyType": "RANGE"}, + ], + BillingMode="PAY_PER_REQUEST", + ) + + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "Invalid KeySchema: Some index key attribute have no definition" + ) + + with pytest.raises(ClientError) as exc: + dynamodb.create_table( + TableName="test-table", + AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"},], + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + GlobalSecondaryIndexes=[ + { + "IndexName": "gsi_user-items", + "KeySchema": [{"AttributeName": "user", "KeyType": "HASH"}], + "Projection": {"ProjectionType": "ALL"}, + } + ], + BillingMode="PAY_PER_REQUEST", + ) + + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "One or more parameter values were invalid: Some index key attributes are not defined in AttributeDefinitions. Keys: [user], AttributeDefinitions: [id]" + ) + + +@mock_dynamodb2 +def test_create_table_with_redundant_and_missing_attributes(): + dynamodb = boto3.client("dynamodb", region_name="us-east-1") + + with pytest.raises(ClientError) as exc: + dynamodb.create_table( + TableName="test-table", + AttributeDefinitions=[ + {"AttributeName": "created_at", "AttributeType": "N"} + ], + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + BillingMode="PAY_PER_REQUEST", + ) + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "One or more parameter values were invalid: Some index key attributes are not defined in AttributeDefinitions. Keys: [id], AttributeDefinitions: [created_at]" + ) + + with pytest.raises(ClientError) as exc: + dynamodb.create_table( + TableName="test-table", + AttributeDefinitions=[ + {"AttributeName": "id", "AttributeType": "S"}, + {"AttributeName": "created_at", "AttributeType": "N"}, + ], + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + GlobalSecondaryIndexes=[ + { + "IndexName": "gsi_user-items", + "KeySchema": [{"AttributeName": "user", "KeyType": "HASH"}], + "Projection": {"ProjectionType": "ALL"}, + } + ], + BillingMode="PAY_PER_REQUEST", + ) + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "One or more parameter values were invalid: Some index key attributes are not defined in AttributeDefinitions. Keys: [user], AttributeDefinitions: [created_at, id]" + )