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

dynamoose.undefined in Model.update
Using `dynamoose.undefined` in `Model.update` was not working correctly. This PR fixes that.


Patch:
diff --git a/lib/Model.js b/lib/Model.js
index 1097cd4f..d91cd61a 100644
--- a/lib/Model.js
+++ b/lib/Model.js
@@ -304,15 +304,21 @@ Model.prototype.update = function(keyObj, updateObj, settings = {}, callback) {
 
 				const dynamoType = this.schema.getAttributeTypeDetails(subKey).dynamodbType;
 
-				if (!updateType.attributeOnly) {
+				const dynamooseUndefined = require("./index").undefined;
+				if (!updateType.attributeOnly && subValue !== dynamooseUndefined) {
 					subValue = (await this.Document.objectFromSchema({[subKey]: dynamoType === "L" && !Array.isArray(subValue) ? [subValue] : subValue}, {"type": "toDynamo", "customTypesDynamo": true, ...updateType.objectFromSchemaSettings}))[subKey];
 				}
 
-				const defaultValue = await this.schema.defaultCheck(subKey, undefined, updateType.objectFromSchemaSettings);
-				if (defaultValue) {
-					subValue = defaultValue;
-					updateType = updateTypes.find((a) => a.name === "$SET");
+				if (subValue !== dynamooseUndefined) {
+					const defaultValue = await this.schema.defaultCheck(subKey, undefined, updateType.objectFromSchemaSettings);
+					if (defaultValue) {
+						subValue = defaultValue;
+						updateType = updateTypes.find((a) => a.name === "$SET");
+					}
+				} else {
+					updateType = updateTypes.find((a) => a.name === "$REMOVE");
 				}
+
 				if (updateType.objectFromSchemaSettings.required) {
 					await this.schema.requiredCheck(subKey, undefined);
 				}


Test patch:
diff --git a/test/Model.js b/test/Model.js
index cc8bb04e..ee56fb83 100644
--- a/test/Model.js
+++ b/test/Model.js
@@ -1283,6 +1283,69 @@ describe("Model", () => {
 					});
 				});
 
+				it("Should send correct params to updateItem when using dynamoose.undefined to delete default property", async () => {
+					updateItemFunction = () => Promise.resolve({});
+					User = new dynamoose.Model("User", {"id": Number, "name": {"type": String, "default": () => "Charlie"}, "age": Number});
+					await callType.func(User).bind(User)({"id": 1, "name": dynamoose.undefined});
+					expect(updateItemParams).to.be.an("object");
+					expect(updateItemParams).to.eql({
+						"ExpressionAttributeNames": {
+							"#a0": "name"
+						},
+						"ExpressionAttributeValues": {},
+						"UpdateExpression": "REMOVE #a0",
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User",
+						"ReturnValues": "ALL_NEW"
+					});
+				});
+
+				it("Should send correct params to updateItem when using dynamoose.undefined to delete default property using $REMOVE", async () => {
+					updateItemFunction = () => Promise.resolve({});
+					User = new dynamoose.Model("User", {"id": Number, "name": {"type": String, "default": () => "Charlie"}, "age": Number});
+					await callType.func(User).bind(User)({"id": 1}, {"$REMOVE": {"name": dynamoose.undefined}});
+					expect(updateItemParams).to.be.an("object");
+					expect(updateItemParams).to.eql({
+						"ExpressionAttributeNames": {
+							"#a0": "name"
+						},
+						"ExpressionAttributeValues": {},
+						"UpdateExpression": "REMOVE #a0",
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User",
+						"ReturnValues": "ALL_NEW"
+					});
+				});
+
+				it("Should send correct params to updateItem when using dynamoose.undefined to delete default property using $SET", async () => {
+					updateItemFunction = () => Promise.resolve({});
+					User = new dynamoose.Model("User", {"id": Number, "name": {"type": String, "default": () => "Charlie"}, "age": Number});
+					await callType.func(User).bind(User)({"id": 1}, {"$SET": {"name": dynamoose.undefined}});
+					expect(updateItemParams).to.be.an("object");
+					expect(updateItemParams).to.eql({
+						"ExpressionAttributeNames": {
+							"#a0": "name"
+						},
+						"ExpressionAttributeValues": {},
+						"UpdateExpression": "REMOVE #a0",
+						"Key": {
+							"id": {
+								"N": "1"
+							}
+						},
+						"TableName": "User",
+						"ReturnValues": "ALL_NEW"
+					});
+				});
+
 				it("Should send correct params to updateItem with $SET update expression", async () => {
 					updateItemFunction = () => Promise.resolve({});
 					await callType.func(User).bind(User)({"id": 1}, {"$SET": {"name": "Tim"}});