Repository: dynamoosejs/dynamoose
Pull request #750 (2020-03-21 20:25:21)

Unnecessary attributes in key
This PR fixes a glitch where passing an entire document into `Model.get` or `Model.delete` would fail due to extra attributes in the key.


Patch:
diff --git a/docs/api/Model.md b/docs/api/Model.md
index a43f610e..03f984a3 100644
--- a/docs/api/Model.md
+++ b/docs/api/Model.md
@@ -196,6 +196,27 @@ User.get({"id": 1, "name": "Tim"}, (error, myUser) => {
 });
 ```
 
+```js
+const User = new dynamoose.Model("User", {"id": Number, "name": String});
+
+try {
+	const myUser = await User.get({"id": 1});
+	console.log(myUser);
+} catch (error) {
+	console.error(error);
+}
+
+// OR
+
+User.get({"id": 1}, (error, myUser) => {
+	if (error) {
+		console.error(error);
+	} else {
+		console.log(myUser);
+	}
+});
+```
+
 ## Model.create(document, [settings], [callback])
 
 This function lets you create a new document for a given model. This function is almost identical to creating a new document and calling `document.save`, with one key difference, this function will default to setting `overwrite` to false.
@@ -357,6 +378,27 @@ User.get({"id": 1, "name": "Tim"}, (error) => {
 });
 ```
 
+```js
+const User = new dynamoose.Model("User", {"id": Number, "name": String});
+
+try {
+	await User.get({"id": 1});
+	console.log("Successfully deleted item");
+} catch (error) {
+	console.error(error);
+}
+
+// OR
+
+User.get({"id": 1}, (error) => {
+	if (error) {
+		console.error(error);
+	} else {
+		console.log("Successfully deleted item");
+	}
+});
+```
+
 ## Model.transaction
 
 This object has the following methods that you can call.
diff --git a/lib/Model.js b/lib/Model.js
index d91cd61a..e3dd34d0 100644
--- a/lib/Model.js
+++ b/lib/Model.js
@@ -210,9 +210,23 @@ async function updateTable(model) {
 }
 
 function convertObjectToKey(key) {
-	return this.Document.toDynamo(typeof key === "object" ? key : {
-		[this.schema.getHashKey()]: key
-	});
+	let keyObject;
+	const hashKey = this.schema.getHashKey();
+	if (typeof key === "object") {
+		const rangeKey = this.schema.getRangeKey();
+		keyObject = {
+			[hashKey]: key[hashKey]
+		};
+		if (rangeKey && key[rangeKey]) {
+			keyObject[rangeKey] = key[rangeKey];
+		}
+	} else {
+		keyObject = {
+			[hashKey]: key
+		};
+	}
+
+	return this.Document.toDynamo(keyObject);
 }
 
 


Test patch:
diff --git a/test/Model.js b/test/Model.js
index ee56fb83..491e173b 100644
--- a/test/Model.js
+++ b/test/Model.js
@@ -661,6 +661,21 @@ describe("Model", () => {
 
 				it("Should send correct params to getItem if we pass in an object", async () => {
 					getItemFunction = () => Promise.resolve({"Item": {"id": {"N": "1"}, "name": {"S": "Charlie"}}});
+					await callType.func(User).bind(User)({"id": 1});
+					expect(getItemParams).to.be.an("object");
+					expect(getItemParams).to.eql({
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User"
+					});
+				});
+
+				it("Should send correct params to getItem if we pass in an object with range key", async () => {
+					getItemFunction = () => Promise.resolve({"Item": {"id": {"N": "1"}, "name": {"S": "Charlie"}}});
+					User = new dynamoose.Model("User", {"id": Number, "name": {"type": String, "rangeKey": true}});
 					await callType.func(User).bind(User)({"id": 1, "name": "Charlie"});
 					expect(getItemParams).to.be.an("object");
 					expect(getItemParams).to.eql({
@@ -676,6 +691,20 @@ describe("Model", () => {
 					});
 				});
 
+				it("Should send correct params to getItem if we pass in an entire object with unnecessary attributes", async () => {
+					getItemFunction = () => Promise.resolve({"Item": {"id": {"N": "1"}, "name": {"S": "Charlie"}}});
+					await callType.func(User).bind(User)({"id": 1, "name": "Charlie"});
+					expect(getItemParams).to.be.an("object");
+					expect(getItemParams).to.eql({
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User"
+					});
+				});
+
 				it("Should return object with correct values", async () => {
 					getItemFunction = () => Promise.resolve({"Item": {"id": {"N": "1"}, "name": {"S": "Charlie"}}});
 					const user = await callType.func(User).bind(User)(1);
@@ -2079,6 +2108,21 @@ describe("Model", () => {
 
 				it("Should send correct params to deleteItem if we pass in an object", async () => {
 					deleteItemFunction = () => Promise.resolve();
+					await callType.func(User).bind(User)({"id": 1});
+					expect(deleteItemParams).to.be.an("object");
+					expect(deleteItemParams).to.eql({
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User"
+					});
+				});
+
+				it("Should send correct params to deleteItem if we pass in an object with range key", async () => {
+					deleteItemFunction = () => Promise.resolve();
+					User = new dynamoose.Model("User", {"id": Number, "name": {"type": String, "rangeKey": true}});
 					await callType.func(User).bind(User)({"id": 1, "name": "Charlie"});
 					expect(deleteItemParams).to.be.an("object");
 					expect(deleteItemParams).to.eql({
@@ -2094,6 +2138,21 @@ describe("Model", () => {
 					});
 				});
 
+				it("Should send correct params to deleteItem if we pass in an entire object with unnecessary attributes", async () => {
+					deleteItemFunction = () => Promise.resolve();
+					await callType.func(User).bind(User)({"id": 1, "name": "Charlie"});
+					expect(deleteItemParams).to.be.an("object");
+					expect(deleteItemParams).to.eql({
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User"
+					});
+				});
+
+
 				it("Should return request if return request setting is set", async () => {
 					const result = await callType.func(User).bind(User)(1, {"return": "request"});
 					expect(deleteItemParams).to.not.exist;