UNPKG

@barchart/common-node-js

Version:

Common classes, utilities, and functions for building Node.js servers

373 lines (303 loc) 12.7 kB
const assert = require('@barchart/common-js/lang/assert'), Enum = require('@barchart/common-js/lang/Enum'), is = require('@barchart/common-js/lang/is'); const DataType = require('./../definitions/DataType'), ComponentType = require('./../definitions/ComponentType'), IndexType = require('./../definitions/IndexType'), KeyType = require('./../definitions/KeyType'), ProjectionType = require('./../definitions/ProjectionType'), ProvisionedThroughput = require('./../definitions/ProvisionedThroughput'), StreamViewType = require('./../definitions/StreamViewType'), Table = require('./../definitions/Table'); const AttributeBuilder = require('./AttributeBuilder'), ComponentBuilder = require('./ComponentBuilder'), IndexBuilder = require('./IndexBuilder'), KeyBuilder = require('./KeyBuilder'), ProvisionedThroughputBuilder = require('./ProvisionedThroughputBuilder'); const LambdaStage = require('../../../lambda/LambdaStage'); module.exports = (() => { 'use strict'; /** * Fluent interface for building a {@link Table}. * * @public * @param {String} name - Name of the table. */ class TableBuilder { constructor(name) { assert.argumentIsRequired(name, 'name', String); this._table = new Table(name, [ ], [ ], [ ], [ ], null, null, null); } /** * The {@link Table}, given all the information provided thus far. * * @public * @returns {Table} */ get table() { return this._table; } /** * Adds a logic for specific environment (via a callback that fires * if the current configuration applies to the desired environment). * * @public * @param {LambdaStage} stage * @param {TableBuilder~stageCallback} callback * @return {TableBuilder} */ forStage(stage, callback) { assert.argumentIsRequired(stage, 'stage', LambdaStage, 'LambdaStage'); assert.argumentIsRequired(callback, 'callback', Function); if (LambdaStage.getStageFromName(this._table.name) === stage) { callback(this); } return this; } /** * Adds an {@link Attribute} and returns the current instance. * * @public * @param {String} attributeName * @param {DataType} dataType * @param {KeyType=} keyType * @returns {TableBuilder} */ withAttribute(attributeName, dataType, keyType) { this.withAttributeBuilder(attributeName, ab => ab.withDataType(dataType)); if (keyType) { this.withKey(attributeName, keyType); } return this; } /** * Adds an {@link Attribute} to the table, using a callback that * provides the consumer with an {@link AttributeBuilder}, then returns * the current instance. * * @public * @param {String} attributeName * @param {TableBuilder~attributeBuilderCallback} callback - Synchronously called, providing a {@link AttributeBuilder} tied to the current instance. * @returns {TableBuilder} */ withAttributeBuilder(attributeName, callback) { assert.argumentIsRequired(callback, 'callback', Function); const attributeBuilder = new AttributeBuilder(attributeName, this); callback(attributeBuilder); const attribute = attributeBuilder.attribute; const attributes = this._table.attributes.filter(a => a.name !== attribute.name).concat(attribute); this._table = new Table(this._table.name, this._table.keys, this._table.indices, attributes, this._table.components, this._table.provisionedThroughput, this._table.streamViewType, this._table.ttlAttribute); return this; } /** * Adds a {@link Component} and returns the current instance. * * @public * @param {String} componentName * @param {ComponentType} componentType * @returns {TableBuilder} */ withComponent(componentName, componentType) { return this.withComponentBuilder(componentName, cb => cb.withComponentType(componentType)); } /** * Adds a {@link Component} to the table, using a callback that * provides the consumer with a {@link ComponentBuilder}, then returns * the current instance. * * @public * @param {String} componentName * @param {TableBuilder~componentBuilderCallback} callback - Synchronously called, providing a {@link ComponentBuilder} tied to the current instance. * @returns {TableBuilder} */ withComponentBuilder(componentName, callback) { assert.argumentIsRequired(callback, 'callback', Function); const componentBuilder = new ComponentBuilder(componentName, this); callback(componentBuilder); const component = componentBuilder.component; const components = this._table.components.filter(c => c.name !== component.name).concat(component); this._table = new Table(this._table.name, this._table.keys, this._table.indices, this._table.attributes, components, this._table.provisionedThroughput, this._table.streamViewType, this._table.ttlAttribute); return this; } /** * Adds a {@link Key} and returns the current instance. * * @public * @param {String} keyName * @param {KeyType} keyType * @returns {TableBuilder} */ withKey(keyName, keyType) { return this.withKeyBuilder(keyName, kb => kb.withKeyType(keyType)); } /** * Adds a {@link Key} to the table, using a callback that * provides the consumer with a {@link KeyBuilder}, then returns * the current instance. * * @public * @param {String} keyName * @param {Function} callback - Synchronously called, providing a {@link KeyBuilder} tied to the current instance. * @returns {TableBuilder} */ withKeyBuilder(keyName, callback) { assert.argumentIsRequired(callback, 'callback', Function); const keyBuilder = new KeyBuilder(keyName, this); callback(keyBuilder); const key = keyBuilder.key; const keys = this._table.keys.filter(k => k.attribute.name !== key.attribute.name).concat(key); this._table = new Table(this._table.name, keys, this._table.indices, this._table.attributes, this._table.components, this._table.provisionedThroughput, this._table.streamViewType, this._table.ttlAttribute); return this; } /** * Adds an {@link Index} to the table, using a callback that * provides the consumer with an {@link IndexBuilder}, then returns * the current instance. * * @public * @param {String} indexName * @param {Function} callback - Synchronously called, providing a {@link IndexBuilder} tied to the current instance. * @returns {TableBuilder} */ withIndexBuilder(indexName, callback) { assert.argumentIsRequired(callback, 'callback', Function); const indexBuilder = new IndexBuilder(indexName, this); callback(indexBuilder); const index = indexBuilder.index; const indices = this._table._indices.filter(i => i.name !== index.name).concat(index); this._table = new Table(this._table.name, this._table.keys, indices, this._table.attributes, this._table.components, this._table.provisionedThroughput, this._table.streamViewType, this._table.ttlAttribute); return this; } /** * Adds a {@link ProvisionedThroughput} specification and returns the * current instance. * * @public * @param {Number} readUnits * @param {Number} writeUnits * @returns {TableBuilder} */ withProvisionedThroughput(readUnits, writeUnits) { return this.withProvisionedThroughputBuilder(ptb => ptb.withRead(readUnits).withWrite(writeUnits)); } /** * Adds an {@link ProvisionedThroughput} specification to the * table, using a callback that provides the consumer with a * {@link ProvisionedThroughputBuilder}, then returns the current instance. * * @public * @param {Function} callback - Synchronously called, providing a {@link ProvisionedThroughputBuilder} tied to the current instance. * @returns {TableBuilder} */ withProvisionedThroughputBuilder(callback) { assert.argumentIsRequired(callback, 'callback', Function); const provisionedThroughputBuilder = new ProvisionedThroughputBuilder(); callback(provisionedThroughputBuilder); this._table = new Table(this._table.name, this._table.keys, this._table.indices, this._table.attributes, this._table.components, provisionedThroughputBuilder.provisionedThroughput, this._table.streamViewType, this._table.ttlAttribute); return this; } /** * Indicates the table should use on-demand throughput (i.e. pricing), instead of * provisioned throughput. * * @public * @returns {TableBuilder} */ withOnDemandThroughput() { this._table = new Table(this._table.name, this._table.keys, this._table.indices, this._table.attributes, this._table.components, null, this._table.streamViewType, this._table.ttlAttribute); return this; } /** * Defines a streaming behavior for the table. * * @public * @param {StreamViewType} streamViewType */ withStreamViewType(streamViewType) { assert.argumentIsRequired(streamViewType, 'streamViewType', StreamViewType, 'StreamViewType'); this._table = new Table(this._table.name, this._table.keys, this._table.indices, this._table.attributes, this._table.components, this._table.provisionedThroughput, streamViewType, this._table.ttlAttribute); return this; } /** * Defines field that stores expiration time. * * @public * @param {String} attributeName * @returns {TableBuilder} */ withTimeToLive(attributeName) { assert.argumentIsRequired(attributeName, 'attributeName', String); this._table = new Table(this._table.name, this._table.keys, this._table.indices, this._table.attributes, this._table.components, this._table.provisionedThroughput, this._table.streamViewType, attributeName); return this; } /** * Creates a new {@link TableBuilder}. * * @public * @static * @param {String} name - Name of the table. * @returns {TableBuilder} */ static withName(name) { return new TableBuilder(name); } static fromDefinition(definition) { let tableBuilder = TableBuilder.withName(definition.TableName); if (definition.ProvisionedThroughput) { tableBuilder.withProvisionedThroughput(definition.ProvisionedThroughput.ReadCapacityUnits, definition.ProvisionedThroughput.WriteCapacityUnits); } definition.AttributeDefinitions.reduce((tb, ad) => tb.withAttribute(ad.AttributeName, DataType.fromCode(ad.AttributeType)), tableBuilder); definition.KeySchema.reduce((tb, ks) => tb.withKey(ks.AttributeName, Enum.fromCode(KeyType, ks.KeyType)), tableBuilder); const processIndex = (indexType, indexDefinition) => { return tableBuilder.withIndexBuilder(indexDefinition.IndexName, (indexBuilder) => { indexDefinition.KeySchema.reduce((ib, ks) => ib.withKey(ks.AttributeName, Enum.fromCode(KeyType, ks.KeyType)), indexBuilder); indexBuilder.withType(indexType) .withProjectionBuilder(Enum.fromCode(ProjectionType, indexDefinition.Projection.ProjectionType), (projectionBuilder) => { if (is.array(indexDefinition.Projection.NonKeyAttributes)) { indexDefinition.Projection.NonKeyAttributes.reduce((pb, nka) => pb.withAttribute(nka, true), projectionBuilder); } }); }); }; if (is.array(definition.LocalSecondaryIndexes)) { definition.LocalSecondaryIndexes.reduce((tb, lsi) => processIndex(IndexType.LOCAL_SECONDARY, lsi), tableBuilder); } if (is.array(definition.GlobalSecondaryIndexes)) { definition.GlobalSecondaryIndexes.reduce((tb, gsi) => processIndex(IndexType.GLOBAL_SECONDARY, gsi), tableBuilder); } if (is.object(definition.StreamSpecification) && is.boolean(definition.StreamSpecification.StreamEnabled) && definition.StreamSpecification.StreamEnabled) { tableBuilder.withStreamViewType(Enum.fromCode(StreamViewType, definition.StreamSpecification.StreamViewType)); } if (is.object(definition.TimeToLiveDescription) && ['ENABLED', 'ENABLING'].includes(definition.TimeToLiveDescription.TimeToLiveStatus)) { tableBuilder.withTimeToLive(definition.TimeToLiveDescription.AttributeName); } return tableBuilder.table; } toString() { return '[TableBuilder]'; } } /** * A callback that provides the consumer with an {@link AttributeBuilder} * * @public * @callback TableBuilder~attributeBuilderCallback * @param {AttributeBuilder} attributeBuilder */ /** * A callback that provides the consumer with a {@link ComponentBuilder} * * @public * @callback TableBuilder~componentBuilderCallback * @param {AttributeBuilder} attributeBuilder */ /** * A callback that provides the consumer with a {@link TableBuilder} -- assuming * the configuration applies to the correct environment (i.e. {@link LambdaStage}). * * @public * @callback TableBuilder~stageCallback * @param {TableBuilder} tableBuilder */ return TableBuilder; })();