diff --git a/README.md b/README.md index e7eaf9b8e..5db5f422e 100755 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Below you will find the current branch strategy for the project. Work taking pla | Branch | Version | NPM Tag | Links | | --- | --- | --- | --- | -| [`v3`](https://github.com/dynamoose/dynamoose/tree/v3) | 3.0.0 | | - [Documentation](https://dynamoose-git-v3-dynamoose.vercel.app/) | +| [`v3`](https://github.com/dynamoose/dynamoose/tree/v3) | 3.0.0 | alpha | - [Documentation](https://dynamoose-git-v3-dynamoose.vercel.app/) | | [`master`](https://github.com/dynamoose/dynamoose/tree/master) | 2.7.x | | - [Documentation](https://dynamoose.now.sh/) | | [`v2.7.1` (tag)](https://github.com/dynamoose/dynamoose/tree/v2.7.1) | 2.7.1 | latest | - [Documentation](https://dynamoosejs.com) diff --git a/lib/Document.ts b/lib/Document.ts index c4b6bc675..5172fc713 100644 --- a/lib/Document.ts +++ b/lib/Document.ts @@ -146,8 +146,11 @@ export class Document { settings = {}; } + let savedItem; + const localSettings: DocumentSaveSettings = settings; const paramsPromise = this.toDynamo({"defaults": true, "validate": true, "required": true, "enum": true, "forceDefault": true, "combine": true, "saveUnknown": true, "customTypesDynamo": true, "updateTimestamps": true, "modifiers": ["set"]}).then((item) => { + savedItem = item; let putItemObj: DynamoDB.PutItemInput = { "Item": item, "TableName": this.model.name @@ -188,13 +191,22 @@ export class Document { if (callback) { const localCallback: CallbackType = callback as CallbackType; promise.then(() => { - this[internalProperties].storedInDynamo = true; localCallback(null, this); + this[internalProperties].storedInDynamo = true; + + const returnDocument = new this.model.Document(savedItem as any); + returnDocument[internalProperties].storedInDynamo = true; + + localCallback(null, returnDocument); }).catch((error) => callback(error)); } else { return (async (): Promise => { await promise; this[internalProperties].storedInDynamo = true; - return this; + + const returnDocument = new this.model.Document(savedItem as any); + returnDocument[internalProperties].storedInDynamo = true; + + return returnDocument; })(); } } @@ -329,7 +341,7 @@ Document.objectFromSchema = async function (object: any, model: Model, if (existsInSchema) { const {isValidType, matchedTypeDetails, typeDetailsArray} = utils.dynamoose.getValueTypeCheckResult(schema, value, genericKey, settings, {"standardKey": true, typeIndexOptionMap}); if (!isValidType) { - throw new Error.TypeMismatch(`Expected ${key} to be of type ${typeDetailsArray.map((detail) => detail.dynamicName ? detail.dynamicName() : detail.name.toLowerCase()).join(", ")}, instead found type ${typeof value}${typeDetailsArray.some((val) => val.name === "Constant") ? ` (${value})` : ""}.`); + throw new Error.TypeMismatch(`Expected ${key} to be of type ${typeDetailsArray.map((detail) => detail.dynamicName ? detail.dynamicName() : detail.name.toLowerCase()).join(", ")}, instead found type ${utils.type_name(value, typeDetailsArray)}.`); } else if (matchedTypeDetails.isSet || matchedTypeDetails.name.toLowerCase() === "model") { validParents.push({key, "infinite": true}); } else if (/*typeDetails.dynamodbType === "M" || */matchedTypeDetails.dynamodbType === "L") { diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 7ccb1895c..21ac91439 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -9,6 +9,7 @@ import empty_function = require("./empty_function"); import object = require("./object"); import dynamoose = require("./dynamoose"); import all_elements_match from "./all_elements_match"; +import type_name from "./type_name"; export = { combine_objects, @@ -21,5 +22,6 @@ export = { array_flatten, empty_function, object, - dynamoose + dynamoose, + type_name }; diff --git a/lib/utils/type_name.ts b/lib/utils/type_name.ts new file mode 100644 index 000000000..f9c247bfc --- /dev/null +++ b/lib/utils/type_name.ts @@ -0,0 +1,16 @@ +import {DynamoDBSetTypeResult, DynamoDBTypeResult} from "../Schema"; + +// This function takes in a value and returns a user string for the type of that value. This function is mostly used to display type errors to users. +export default (value: any, typeDetailsArray: (DynamoDBTypeResult | DynamoDBSetTypeResult)[]): string => { + let str = ""; + if (value === null) { + str += "null"; + } else { + str += typeof value; + } + + // Add constant value to type name + str += typeDetailsArray.some((val) => val.name === "Constant") ? ` (${value})` : ""; + + return str; +}; diff --git a/test/unit/Document.js b/test/unit/Document.js index ec79d81f8..9b07a46ba 100644 --- a/test/unit/Document.js +++ b/test/unit/Document.js @@ -170,6 +170,16 @@ describe("Document", () => { expect(result).to.eql(user); }); + it("Should return correct result after saving with defaults", async () => { + putItemFunction = () => Promise.resolve(); + + User = dynamoose.model("User", {"id": Number, "name": String, "defaultValue": {"type": String, "default": "Hello World"}}); + user = new User({"id": 1, "name": "Charlie"}); + + const result = await callType.func(user).bind(user)(); + expect(result.toJSON()).to.eql({"id": 1, "name": "Charlie", "defaultValue": "Hello World"}); + }); + it("Should return request if return request is set as setting", async () => { const result = await callType.func(user).bind(user)({"return": "request"}); expect(putParams).to.eql([]); @@ -2740,6 +2750,11 @@ describe("Document", () => { return {"id": Number, "data": [{"type": Set, "schema": [Item]}, String]}; }, "error": new Error.ValidationError("Expected data to be of type Item Set, string, instead found type boolean.") + }, + { + "input": [{"id": 1, "data": null}, {"type": "toDynamo"}], + "schema": {"id": Number, "data": String}, + "error": new Error.ValidationError("Expected data to be of type string, instead found type null.") } ]; diff --git a/test/unit/Model.js b/test/unit/Model.js index ce13a6660..d2a595e98 100644 --- a/test/unit/Model.js +++ b/test/unit/Model.js @@ -2102,6 +2102,15 @@ describe("Model", () => { ]; functionCallTypes.forEach((callType) => { describe(callType.name, () => { + it("Should return correct result after saving with defaults", async () => { + createItemFunction = () => Promise.resolve(); + + User = dynamoose.model("User", {"id": Number, "name": String, "defaultValue": {"type": String, "default": "Hello World"}}); + + const result = await callType.func(User).bind(User)({"id": 1, "name": "Charlie"}); + expect(result.toJSON()).to.eql({"id": 1, "name": "Charlie", "defaultValue": "Hello World"}); + }); + it("Should send correct params to putItem", async () => { createItemFunction = () => Promise.resolve(); await callType.func(User).bind(User)({"id": 1, "name": "Charlie"});