Repository: dynamoosejs/dynamoose
Pull request #733 (2020-03-17 00:16:56)

Model Initialization Creation Error


Patch:
diff --git a/lib/Model.js b/lib/Model.js
index 67879109..6c9986ca 100644
--- a/lib/Model.js
+++ b/lib/Model.js
@@ -49,24 +49,24 @@ class Model {
 		const setupFlow = []; // An array of setup actions to be run in order
 		// Create table
 		if (this.options.create) {
-			setupFlow.push(createTable(this));
+			setupFlow.push(() => createTable(this));
 		}
 		// Wait for Active
 		if ((this.options.waitForActive || {}).enabled) {
-			setupFlow.push(waitForActive(this));
+			setupFlow.push(() => waitForActive(this));
 		}
 		// Update Time To Live
 		if ((this.options.create || this.options.update) && options.expires) {
-			setupFlow.push(updateTimeToLive(this));
+			setupFlow.push(() => updateTimeToLive(this));
 		}
 		// Update
 		if (this.options.update) {
-			setupFlow.push(updateTable(this));
+			setupFlow.push(() => updateTable(this));
 		}
 
 		// Run setup flow
 		const setupFlowPromise = setupFlow.reduce((existingFlow, flow) => {
-			return existingFlow.then(() => flow).then((flow) => {
+			return existingFlow.then(() => flow()).then((flow) => {
 				const flowItem = typeof flow === "function" ? flow : flow.promise;
 				return flowItem instanceof Promise ? flowItem : flowItem.bind(flow)();
 			});
@@ -123,7 +123,7 @@ class Model {
 
 // Utility functions
 async function createTable(model) {
-	if ((((await getTableDetails(model)) || {}).Table || {}).TableStatus === "ACTIVE") {
+	if ((((await getTableDetails(model, {"allowError": true})) || {}).Table || {}).TableStatus === "ACTIVE") {
 		return {"promise": () => Promise.resolve()};
 	}
 
@@ -165,7 +165,7 @@ function waitForActive(model) {
 		async function check(count) {
 			try {
 				// Normally we'd want to do `dynamodb.waitFor` here, but since it doesn't work with tables that are being updated we can't use it in this case
-				if ((await getTableDetails(model, count > 0)).Table.TableStatus === "ACTIVE") {
+				if ((await getTableDetails(model, {"forceRefresh": count > 0})).Table.TableStatus === "ACTIVE") {
 					return resolve();
 				}
 			} catch (e) {
@@ -184,10 +184,19 @@ function waitForActive(model) {
 		check(0);
 	});
 }
-async function getTableDetails(model, forceRefresh = false) {
-	if (forceRefresh || !model.latestTableDetails) {
+async function getTableDetails(model, settings = {}) {
+	const func = async () => {
 		const tableDetails = await aws.ddb().describeTable({"TableName": model.name}).promise();
 		model.latestTableDetails = tableDetails; // eslint-disable-line require-atomic-updates
+	};
+	if (settings.forceRefresh || !model.latestTableDetails) {
+		if (settings.allowError) {
+			try {
+				await func();
+			} catch (e) {} // eslint-disable-line no-empty
+		} else {
+			await func();
+		}
 	}
 
 	return model.latestTableDetails;


Test patch:
diff --git a/test/Model.js b/test/Model.js
index e3c86747..4c44201e 100644
--- a/test/Model.js
+++ b/test/Model.js
@@ -54,6 +54,39 @@ describe("Model", () => {
 			expect(Cat.Model.schema).to.be.an.instanceof(dynamoose.Schema);
 		});
 
+		it("Should not fail with initialization if table doesn't exist", async () => {
+			dynamoose.Model.defaults = {};
+			const itemsCalled = [];
+			dynamoose.aws.ddb.set({
+				"createTable": () => {
+					return {
+						"promise": () => new Promise((resolve) => {
+							itemsCalled.push("createTable");
+							setTimeout(() => {
+								itemsCalled.push("createTableDone");
+								resolve();
+							}, 100);
+						})
+					};
+				},
+				"describeTable": () => ({"promise": () => {
+					itemsCalled.push("describeTable");
+					return itemsCalled.includes("createTableDone") ? Promise.resolve({"Table": {"TableStatus": "ACTIVE"}}) : Promise.reject();
+				}})
+			});
+
+			const tableName = "Cat";
+			let failed = false;
+			const errorHandler = () => {
+				failed = true;
+			};
+			process.on("unhandledRejection", errorHandler);
+			new dynamoose.Model(tableName, {"id": String});
+			await utils.timeout(100);
+			expect(failed).to.be.false;
+			process.removeListener("unhandledRejection", errorHandler);
+		});
+
 		// Prefixes & Suffixes
 		const optionsB = [
 			{"name": "Prefix", "value": "prefix", "check": (val, result) => expect(result).to.match(new RegExp(`^${val}`))},