DynamoDB (V1) - implement UpdateItem (#3926)
* DynamoDB (V1) - UpdateItem implementation * DynamoDB (V1) - negative tests for UpdateItem
This commit is contained in:
parent
fbd93efdb7
commit
bb3cbd0bb4
@ -38,6 +38,12 @@ class DynamoType(object):
|
||||
def __repr__(self):
|
||||
return "DynamoType: {0}".format(self.to_json())
|
||||
|
||||
def add(self, dyn_type):
|
||||
if self.type == "SS":
|
||||
self.value.append(dyn_type.value)
|
||||
if self.type == "N":
|
||||
self.value = str(int(self.value) + int(dyn_type.value))
|
||||
|
||||
def to_json(self):
|
||||
return {self.type: self.value}
|
||||
|
||||
@ -281,6 +287,20 @@ class Table(CloudFormationModel):
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def update_item(self, hash_key, range_key, attr_updates):
|
||||
item = self.get_item(hash_key, range_key)
|
||||
if not item:
|
||||
return None
|
||||
|
||||
for attr, update in attr_updates.items():
|
||||
if update["Action"] == "PUT":
|
||||
item.attrs[attr] = DynamoType(update["Value"])
|
||||
if update["Action"] == "DELETE":
|
||||
item.attrs.pop(attr)
|
||||
if update["Action"] == "ADD":
|
||||
item.attrs[attr].add(DynamoType(update["Value"]))
|
||||
return item
|
||||
|
||||
def get_cfn_attribute(self, attribute_name):
|
||||
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||
|
||||
@ -360,5 +380,15 @@ class DynamoDBBackend(BaseBackend):
|
||||
|
||||
return table.delete_item(hash_key, range_key)
|
||||
|
||||
def update_item(self, table_name, hash_key_dict, range_key_dict, attr_updates):
|
||||
table = self.tables.get(table_name)
|
||||
if not table:
|
||||
return None
|
||||
|
||||
hash_key = DynamoType(hash_key_dict)
|
||||
range_key = DynamoType(range_key_dict) if range_key_dict else None
|
||||
|
||||
return table.update_item(hash_key, range_key, attr_updates)
|
||||
|
||||
|
||||
dynamodb_backend = DynamoDBBackend()
|
||||
|
@ -275,3 +275,22 @@ class DynamoHandler(BaseResponse):
|
||||
else:
|
||||
er = "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException"
|
||||
return self.error(er)
|
||||
|
||||
def update_item(self):
|
||||
name = self.body["TableName"]
|
||||
key = self.body["Key"]
|
||||
hash_key = key["HashKeyElement"]
|
||||
range_key = key.get("RangeKeyElement")
|
||||
updates = self.body["AttributeUpdates"]
|
||||
return_values = self.body.get("ReturnValues", "") # noqa
|
||||
|
||||
item = dynamodb_backend.update_item(name, hash_key, range_key, updates)
|
||||
|
||||
if item:
|
||||
item_dict = item.to_json()
|
||||
item_dict["ConsumedCapacityUnits"] = 0.5
|
||||
|
||||
return dynamo_json_dump(item_dict)
|
||||
else:
|
||||
er = "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException"
|
||||
return self.error(er)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
import json
|
||||
import sure # noqa
|
||||
|
||||
import moto.server as server
|
||||
@ -18,3 +19,169 @@ def test_table_list():
|
||||
headers = {"X-Amz-Target": "TestTable.ListTables"}
|
||||
res = test_client.get("/", headers=headers)
|
||||
res.data.should.contain(b"TableNames")
|
||||
|
||||
|
||||
def test_update_item():
|
||||
backend = server.create_backend_app("dynamodb")
|
||||
test_client = backend.test_client()
|
||||
|
||||
create_table(test_client)
|
||||
|
||||
headers, res = put_item(test_client)
|
||||
|
||||
# UpdateItem
|
||||
headers["X-Amz-Target"] = "DynamoDB_20111205.UpdateItem"
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"Key": {
|
||||
"HashKeyElement": {"S": "customer"},
|
||||
"RangeKeyElement": {"N": "12341234"},
|
||||
},
|
||||
"AttributeUpdates": {"new_att": {"Value": {"SS": ["val"]}, "Action": "PUT"}},
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
|
||||
# UpdateItem
|
||||
headers["X-Amz-Target"] = "DynamoDB_20111205.UpdateItem"
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"Key": {
|
||||
"HashKeyElement": {"S": "customer"},
|
||||
"RangeKeyElement": {"N": "12341234"},
|
||||
},
|
||||
"AttributeUpdates": {"new_n": {"Value": {"N": "42"}, "Action": "PUT"}},
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
res = json.loads(res.data)
|
||||
|
||||
res["ConsumedCapacityUnits"].should.equal(0.5)
|
||||
res["Attributes"].should.equal(
|
||||
{
|
||||
"hkey": "customer",
|
||||
"name": "myname",
|
||||
"rkey": "12341234",
|
||||
"new_att": ["val"],
|
||||
"new_n": "42",
|
||||
}
|
||||
)
|
||||
|
||||
# UpdateItem - multiples
|
||||
headers["X-Amz-Target"] = "DynamoDB_20111205.UpdateItem"
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"Key": {
|
||||
"HashKeyElement": {"S": "customer"},
|
||||
"RangeKeyElement": {"N": "12341234"},
|
||||
},
|
||||
"AttributeUpdates": {
|
||||
"new_n": {"Value": {"N": 7}, "Action": "ADD"},
|
||||
"new_att": {"Value": {"S": "val2"}, "Action": "ADD"},
|
||||
"name": {"Action": "DELETE"},
|
||||
},
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
res = json.loads(res.data)
|
||||
|
||||
res["ConsumedCapacityUnits"].should.equal(0.5)
|
||||
res["Attributes"].should.equal(
|
||||
{
|
||||
"hkey": "customer",
|
||||
"rkey": "12341234",
|
||||
"new_att": ["val", "val2"],
|
||||
"new_n": "49",
|
||||
}
|
||||
)
|
||||
|
||||
# GetItem
|
||||
headers["X-Amz-Target"] = "DynamoDB_20111205.GetItem"
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"Key": {
|
||||
"HashKeyElement": {"S": "customer"},
|
||||
"RangeKeyElement": {"N": "12341234"},
|
||||
},
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
res = json.loads(res.data)
|
||||
res["Item"].should.have.key("new_att").equal({"SS": ["val", "val2"]})
|
||||
res["Item"].should.have.key("new_n").equal({"N": "49"})
|
||||
res["Item"].shouldnt.have.key("name")
|
||||
|
||||
|
||||
def test_update_item_that_doesnt_exist():
|
||||
backend = server.create_backend_app("dynamodb")
|
||||
test_client = backend.test_client()
|
||||
|
||||
create_table(test_client)
|
||||
|
||||
# UpdateItem
|
||||
headers = {"X-Amz-Target": "DynamoDB_20111205.UpdateItem"}
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"Key": {
|
||||
"HashKeyElement": {"S": "customer"},
|
||||
"RangeKeyElement": {"N": "12341234"},
|
||||
},
|
||||
"AttributeUpdates": {"new_att": {"Value": {"SS": ["val"]}, "Action": "PUT"}},
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
res.status_code.should.equal(400)
|
||||
json.loads(res.data).should.equal(
|
||||
{"__type": "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException"}
|
||||
)
|
||||
|
||||
|
||||
def test_update_item_in_nonexisting_table():
|
||||
backend = server.create_backend_app("dynamodb")
|
||||
test_client = backend.test_client()
|
||||
|
||||
# UpdateItem
|
||||
headers = {"X-Amz-Target": "DynamoDB_20111205.UpdateItem"}
|
||||
request_body = {
|
||||
"TableName": "nonexistent",
|
||||
"Key": {
|
||||
"HashKeyElement": {"S": "customer"},
|
||||
"RangeKeyElement": {"N": "12341234"},
|
||||
},
|
||||
"AttributeUpdates": {"new_att": {"Value": {"SS": ["val"]}, "Action": "PUT"}},
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
res.status_code.should.equal(400)
|
||||
json.loads(res.data).should.equal(
|
||||
{"__type": "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException"}
|
||||
)
|
||||
|
||||
|
||||
def put_item(test_client, rkey="12341234"):
|
||||
headers = {
|
||||
"X-Amz-Target": "DynamoDB_20111205.PutItem",
|
||||
"Content-Type": "application/x-amz-json-1.0",
|
||||
}
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"Item": {
|
||||
"hkey": {"S": "customer"},
|
||||
"rkey": {"N": rkey},
|
||||
"name": {"S": "myname"},
|
||||
},
|
||||
"ReturnValues": "ALL_OLD",
|
||||
}
|
||||
res = test_client.post("/", headers=headers, json=request_body)
|
||||
res = json.loads(res.data)
|
||||
return headers, res
|
||||
|
||||
|
||||
def create_table(test_client):
|
||||
headers = {
|
||||
"X-Amz-Target": "DynamoDB_20111205.CreateTable",
|
||||
"Content-Type": "application/x-amz-json-1.0",
|
||||
}
|
||||
request_body = {
|
||||
"TableName": "Table1",
|
||||
"KeySchema": {
|
||||
"HashKeyElement": {"AttributeName": "hkey", "AttributeType": "S"},
|
||||
"RangeKeyElement": {"AttributeName": "rkey", "AttributeType": "N"},
|
||||
},
|
||||
"ProvisionedThroughput": {"ReadCapacityUnits": 5, "WriteCapacityUnits": 10},
|
||||
}
|
||||
return test_client.post("/", headers=headers, json=request_body)
|
||||
|
Loading…
Reference in New Issue
Block a user