@nerdware/ddb-single-table
Version:
A schema-based DynamoDB modeling tool, high-level API, and type-generator built to supercharge single-table designs!⚡
88 lines (87 loc) • 5.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureTableIsActive = void 0;
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
const ts_type_safety_utils_1 = require("@nerdware/ts-type-safety-utils");
const errors_js_1 = require("../utils/errors.js");
/**
* This method of the `Table` class is used to check if a DynamoDB table is active and
* ready for use.
*
* The function checks the DDB table's status using the Table instance's `describeTable`
* method. If the method returns a `ResourceNotFoundException`, the table does not exist.
* If the table does not exist and `createIfNotExists` is set to `true`, the function
* creates the table using the Table instance's `createTable` method — this function then
* continues the process of waiting for the table to become active.
*
* Regardless of whether the table initially existed or not, if it is not active, the
* function tries to connect to it again after `frequency` number of seconds have passed
* until either the table is active, or `maxRetries` number of attempts have been made,
* or `timeout` number of seconds have passed.
*/
const ensureTableIsActive = async function ({ timeout: timeoutSeconds = 30, frequency: frequencySeconds = 1, maxRetries = 20, createIfNotExists = false, } = {}) {
// Get timeout and frequency in milliseconds for use in setTimeout calls
const timeoutMilliseconds = timeoutSeconds * 1000;
const frequencyMilliseconds = frequencySeconds * 1000;
// Start timeout timer that throws error if not cleared within the timeframe.
const timeoutTimerID = setTimeout(() => {
throw new errors_js_1.DdbSingleTableError(`ensureTableIsActive has timed out after ${timeoutSeconds} seconds.`);
}, timeoutMilliseconds);
// Local state var to ensure CreateTable isn't called more than once.
let hasCreateTableBeenCalled = false;
// Try to get TableStatus, ensure it's ACTIVE.
for (let i = 0; i < maxRetries; i++) {
try {
// DescribeTable will throw if Table doesn't exist
const response = await this.describeTable();
const tableStatus = response.Table?.TableStatus;
if (tableStatus === client_dynamodb_1.TableStatus.ACTIVE) {
clearTimeout(timeoutTimerID);
this.isTableActive = true;
break;
}
this.logger(`Table "${this.tableName}" is not ACTIVE. Current table status: ${tableStatus ?? "UNKNOWN"}`);
// Wait `frequencyMilliseconds`, then try again
await new Promise((resolve) => {
setTimeout(resolve, frequencyMilliseconds);
});
}
catch (err) {
// Sanity type-check: ensure `err` is an object.
if (!(0, ts_type_safety_utils_1.isError)(err) && !(0, ts_type_safety_utils_1.isPlainObject)(err))
throw err;
// If err.code is "ECONNREFUSED", a connection could not be made to the provided endpoint.
if (err.code === errors_js_1.DdbConnectionError.NODE_ERROR_CODES.ECONNREFUSED)
throw new errors_js_1.DdbConnectionError(err);
// If `err` is a "ResourceNotFoundException", Table doesn't exist — see if it should be created.
if (err.name !== client_dynamodb_1.ResourceNotFoundException.name)
throw err;
// If Table doesn't exist AND !createIfNotExists, throw error.
if (!createIfNotExists) {
throw new errors_js_1.DdbSingleTableError(`Table "${this.tableName}" not found. To have the table created automatically when `
+ `DynamoDB returns a "ResourceNotFoundException", set "createIfNotExists" to true.`);
}
// Inform user the Table doesn't exist.
this.logger(`Table "${this.tableName}" not found.`);
// If createTable has already been called, continue the for-loop.
if (hasCreateTableBeenCalled === true)
continue;
// Else attempt to create the Table.
this.logger(`Creating Table "${this.tableName}" ...`);
// Create the table (provide `createIfNotExists` if it's a `tableConfigs` object)
const response = await this.createTable((0, ts_type_safety_utils_1.isPlainObject)(createIfNotExists) ? createIfNotExists : undefined);
// Get the TableStatus from the response
const tableStatus = response.TableDescription?.TableStatus;
// Update this bool flag so ensure CreateTable is only ever called once.
hasCreateTableBeenCalled = true;
this.logger(`CreateTable operation complete. Current table status: ${tableStatus ?? "UNKNOWN"}`);
// TableStatus is possibly already ACTIVE if using ddb-local.
if (tableStatus === client_dynamodb_1.TableStatus.ACTIVE) {
clearTimeout(timeoutTimerID);
this.isTableActive = true;
break;
}
}
}
};
exports.ensureTableIsActive = ensureTableIsActive;