@nerdware/ddb-single-table
Version:
A schema-based DynamoDB modeling tool, high-level API, and type-generator built to supercharge single-table designs!⚡
113 lines (112 loc) • 5.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelSchema = void 0;
const ts_type_safety_utils_1 = require("@nerdware/ts-type-safety-utils");
const errors_js_1 = require("../utils/errors.js");
const BaseSchema_js_1 = require("./BaseSchema.js");
const timestampAttributes_js_1 = require("./timestampAttributes.js");
/**
* This class and its `BaseSchema` parent currently only serve to organize schema-related types,
* validation methods, etc., but may be used to create schema instances in the future. This is
* currently not the case, as schema attributes would need to be nested under an instance property
* (e.g. `this.attributes`), which would require a lot of refactoring. If/when this is implemented,
* schema instances would also be given "metadata" props like "name", "version", "schemaType", etc.
*/
class ModelSchema extends BaseSchema_js_1.BaseSchema {
static DEFAULT_OPTIONS = {
allowUnknownAttributes: false,
autoAddTimestamps: false,
};
static TIMESTAMP_ATTRIBUTES = timestampAttributes_js_1.TIMESTAMP_ATTRIBUTES;
/**
* This function validates the provided `modelSchema`, and if valid, returns an
* object specifying the Model's alias maps:
* - `attributesToAliasesMap`
* - `aliasesToAttributesMap`
*
* This function performs the following validation checks:
*
* 1. Ensure ModelSchema does not specify key-attribute configs which are only valid in the
* TableKeysSchema (e.g. "isHashKey", "isRangeKey", "index")
* 2. Ensure all "alias" values are unique
*
* @param modelSchema - The schema to validate.
* @param name - A name to identify the schema in any error messages.
* @returns An object specifying the Model's alias maps.
* @throws `SchemaValidationError` if the provided ModelSchema is invalid.
*/
static validate = (modelSchema, { name: schemaName }) => {
// First run the base Schema validation checks:
BaseSchema_js_1.BaseSchema.validateAttributeTypes(modelSchema, {
schemaType: "ModelSchema",
name: schemaName,
});
// Then run the ModelSchema-specific validation checks:
const attributesToAliasesMap = {};
const aliasesToAttributesMap = {};
for (const attrName in modelSchema) {
const { alias, ...attrConfigs } = modelSchema[attrName];
// Ensure ModelSchema doesn't include key/index configs which are only valid in the TableKeysSchema
["isHashKey", "isRangeKey", "index"].forEach((keyConfigProperty) => {
if ((0, ts_type_safety_utils_1.hasKey)(attrConfigs, keyConfigProperty)) {
throw new errors_js_1.SchemaValidationError({
schemaName,
problem: `attribute "${attrName}" includes an "${keyConfigProperty}" config, which is only valid in the TableKeysSchema`,
});
}
});
// If an "alias" is specified, ensure it's unique, and add it to the alias maps
if (alias) {
// If alias already exists in accum, there's a dupe alias in the schema, throw error
if (alias in aliasesToAttributesMap) {
throw new errors_js_1.SchemaValidationError({
schemaName,
problem: `the ModelSchema contains duplicate alias "${alias}"`,
});
}
// Add attrName/alias to the alias maps
attributesToAliasesMap[attrName] = alias;
aliasesToAttributesMap[alias] = attrName;
}
}
return {
attributesToAliasesMap,
aliasesToAttributesMap,
};
};
/**
* This function provides sorted ModelSchema entries for IO-Actions. After passing the provided
* `modelSchema` into Object.entries, the resulting entries array is sorted as follows:
*
* 1. The `tableHashKey` is sorted to the front.
* 2. The `tableRangeKey`, if present, goes after the `tableHashKey`.
* 3. Index PKs, if any, go after the `tableRangeKey`.
* 4. For all other attributes, the order in the provided `modelSchema` is preserved.
*
* @param modelSchema - The ModelSchema object to use to obtain sorted entries.
* @returns An array of sorted ModelSchema entries.
*/
static getSortedSchemaEntries = (modelSchema, { tableHashKey, tableRangeKey, indexes = {} }) => {
// Create a map of index PKs for quick lookup
const indexPKs = {};
for (const index of Object.values(indexes)) {
indexPKs[index.indexPK] = true;
}
return Object.entries(modelSchema).sort(([attrNameA], [attrNameB]) => {
return attrNameA === tableHashKey // Sort tableHashKey to the front
? -1
: attrNameB === tableHashKey
? 1
: attrNameA === tableRangeKey // tableRangeKey goes after tableHashKey
? -1
: attrNameB === tableRangeKey
? 1
: attrNameA in indexPKs // index PKs, if any, go after tableRangeKey
? -1
: attrNameB in indexPKs
? 1
: 0; // For all other attributes the order is unchanged
});
};
}
exports.ModelSchema = ModelSchema;