UNPKG

amazon-qldb-kvs-nodejs

Version:

A helper module, simplifying basic interactions with Amazon Quantum Ledger Database for Node.js through a simple key-value store interface.

224 lines (220 loc) 16.4 kB
"use strict"; /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.metadataJSONtoIonUint8Array = exports.toWriterBytes = exports.validateStringAsISODateTime = exports.validateAttributeNameConstrains = exports.validateLedgerNameConstrains = exports.validateTableNameConstrains = exports.valueHolderToString = exports.getBlobValue = exports.sleep = exports.digestResponseToString = exports.blockResponseToString = void 0; const ion_js_1 = require("ion-js"); const Logging_1 = require("./Logging"); const logger = Logging_1.log.getLogger("qldb-helper"); /** * Returns the string representation of a given BlockResponse. * @param blockResponse The BlockResponse to convert to string. * @returns The string representation of the supplied BlockResponse. */ function blockResponseToString(blockResponse) { let stringBuilder = ""; if (blockResponse.Block.IonText) { stringBuilder = stringBuilder + "Block: " + blockResponse.Block.IonText + ", "; } if (blockResponse.Proof.IonText) { stringBuilder = stringBuilder + "Proof: " + blockResponse.Proof.IonText; } stringBuilder = "{" + stringBuilder + "}"; const writer = (0, ion_js_1.makePrettyWriter)(); const reader = (0, ion_js_1.makeReader)(stringBuilder); writer.writeValues(reader); return (0, ion_js_1.decodeUtf8)(writer.getBytes()); } exports.blockResponseToString = blockResponseToString; /** * Returns the string representation of a given GetDigestResponse. * @param digestResponse The GetDigestResponse to convert to string. * @returns The string representation of the supplied GetDigestResponse. */ function digestResponseToString(digestResponse) { let stringBuilder = ""; if (digestResponse.Digest) { stringBuilder += "Digest: " + JSON.stringify((0, ion_js_1.toBase64)(digestResponse.Digest)) + ", "; } if (digestResponse.DigestTipAddress.IonText) { stringBuilder += "DigestTipAddress: " + digestResponse.DigestTipAddress.IonText; } stringBuilder = "{" + stringBuilder + "}"; const writer = (0, ion_js_1.makePrettyWriter)(); const reader = (0, ion_js_1.makeReader)(stringBuilder); writer.writeValues(reader); return (0, ion_js_1.decodeUtf8)(writer.getBytes()); } exports.digestResponseToString = digestResponseToString; /** * Sleep for the specified amount of time. * @param ms The amount of time to sleep in milliseconds. * @returns Promise which fulfills with void. */ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } exports.sleep = sleep; /** * Find the value of a given path in an Ion value. The path should contain a blob value. * @param value The Ion value that contains the journal block attributes. * @param path The path to a certain attribute. * @returns Uint8Array value of the blob, or null if the attribute cannot be found in the Ion value * or is not of type Blob */ function getBlobValue(value, path) { const attribute = value.get(path); if (attribute !== null && attribute.getType() === ion_js_1.IonTypes.BLOB) { return attribute.uInt8ArrayValue(); } return null; } exports.getBlobValue = getBlobValue; /** * Returns the string representation of a given ValueHolder. * @param valueHolder The ValueHolder to convert to string. * @returns The string representation of the supplied ValueHolder. */ function valueHolderToString(valueHolder) { const stringBuilder = `{ IonText: ${valueHolder.IonText}}`; const writer = (0, ion_js_1.makePrettyWriter)(); const reader = (0, ion_js_1.makeReader)(stringBuilder); writer.writeValues(reader); return (0, ion_js_1.decodeUtf8)(writer.getBytes()); } exports.valueHolderToString = valueHolderToString; /** * Checks a string for compliance with table naming constrains: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming * @param tableName A string containing a name of a table. * @returns Returns true if string complies with table naming constrains and trows an error if otherwise. */ function validateTableNameConstrains(tableName) { const nameStringRegexTemplate = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/g; const nameStringRegexCheckResult = nameStringRegexTemplate.test(tableName); if (!nameStringRegexCheckResult) { throw new Error(`Please check tableName complies with Amazon QLDB table naming constrains: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming`); } const partiQLReservedWords = ["ABSOLUTE", "ACTION", "ADD", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "AS", "ASC", "ASSERTION", "AT", "AUTHORIZATION", "AVG", "BAG", "BEGIN", "BETWEEN", "BIT", "BIT_LENGTH", "BLOB", "BOOL", "BOOLEAN", "BOTH", "BY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COMMIT", "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONTINUE", "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATE", "DATE_ADD", "DATE_DIFF", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DESCRIBE", "DESCRIPTOR", "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DOMAIN", "DOUBLE", "DROP", "ELSE", "END", "END-EXEC", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FIRST", "FLOAT", "FOR", "FOREIGN", "FOUND", "FROM", "FULL", "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GROUP", "HAVING", "HOUR", "IDENTITY", "IMMEDIATE", "IN", "INDEX", "INDICATOR", "INITIALLY", "INNER", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL", "INTO", "IS", "ISOLATION", "JOIN", "KEY", "LANGUAGE", "LAST", "LEADING", "LEFT", "LEVEL", "LIKE", "LIMIT", "LIST", "LOCAL", "LOWER", "MATCH", "MAX", "MIN", "MINUTE", "MISSING", "MODULE", "MONTH", "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NEXT", "NO", "NOT", "NULL", "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER", "OUTER", "OUTPUT", "OVERLAPS", "PAD", "PARTIAL", "PIVOT", "POSITION", "PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", "READ", "REAL", "REFERENCES", "RELATIVE", "REMOVE", "RESTRICT", "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA", "SCROLL", "SECOND", "SECTION", "SELECT", "SESSION", "SESSION_USER", "SET", "SEXP", "SIZE", "SMALLINT", "SOME", "SPACE", "SQL", "SQLCODE", "SQLERROR", "SQLSTATE", "STRING", "STRUCT", "SUBSTRING", "SUM", "SYMBOL", "SYSTEM_USER", "TABLE", "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TO_STRING", "TO_TIMESTAMP", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", "TRIM", "TRUE", "TUPLE", "TXID", "UNDROP", "UNION", "UNIQUE", "UNKNOWN", "UNPIVOT", "UPDATE", "UPPER", "USAGE", "USER", "USING", "UTCNOW", "VALUE", "VALUES", "VARCHAR", "VARYING", "VIEW", "WHEN", "WHENEVER", "WHERE", "WITH", "WORK", "WRITE", "YEAR", "ZONE"]; const partiQLReservedWordsCheck = partiQLReservedWords.includes(tableName.toUpperCase()); if (partiQLReservedWordsCheck) { throw new Error(`Please check tableName does not contain PartiQL reserved words: https://docs.aws.amazon.com/qldb/latest/developerguide/ql-reference.reserved.html`); } return true; } exports.validateTableNameConstrains = validateTableNameConstrains; /** * Checks a string for compliance with ledger naming constrains: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming * @param ledgerName A string containing a name of a table. * @returns Returns true if string complies with ledger naming constrains and trows an error if otherwise. */ function validateLedgerNameConstrains(ledgerName) { const nameStringRegexTemplate = /^[A-Za-z0-9][A-Za-z0-9-]{0,30}[A-Za-z0-9]{0,1}$/g; const nameStringRegexCheckResult = nameStringRegexTemplate.test(ledgerName); if (!nameStringRegexCheckResult) { throw new Error(`Please check ledgerName complies with Amazon QLDB table naming constrains: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming`); } const nameNonDigitRegexTemplate = /\D/g; const nameNonDigitRegexCheckResult = nameNonDigitRegexTemplate.test(ledgerName); if (!nameNonDigitRegexCheckResult) { throw new Error(`Please check ledgerName - can not be all digits: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming`); } const nameTwoHyphensRegexTemplate = /--/g; const nameTwoHyphensRegexCheckResult = nameTwoHyphensRegexTemplate.test(ledgerName); if (nameTwoHyphensRegexCheckResult) { throw new Error(`Please check ledgerName - can not contain two consecutive hyphens: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming`); } return true; } exports.validateLedgerNameConstrains = validateLedgerNameConstrains; /** * Checks a string for compliance with document attribute naming constrains. * @param attributeName A string containing a name of a document attribute. * @returns Returns true if string complies with attribute naming constrains and trows an error if otherwise. */ function validateAttributeNameConstrains(attributeName) { const nameStringRegexTemplate = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/g; const nameStringRegexCheckResult = nameStringRegexTemplate.test(attributeName); if (!nameStringRegexCheckResult) { throw new Error(`Please check attributeName complies with Amazon QLDB table naming constrains: https://docs.aws.amazon.com/qldb/latest/developerguide/limits.html#limits.naming`); } const partiQLReservedWords = ["ABSOLUTE", "ACTION", "ADD", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "AS", "ASC", "ASSERTION", "AT", "AUTHORIZATION", "AVG", "BAG", "BEGIN", "BETWEEN", "BIT", "BIT_LENGTH", "BLOB", "BOOL", "BOOLEAN", "BOTH", "BY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COMMIT", "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONTINUE", "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATE", "DATE_ADD", "DATE_DIFF", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DESCRIBE", "DESCRIPTOR", "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DOMAIN", "DOUBLE", "DROP", "ELSE", "END", "END-EXEC", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FIRST", "FLOAT", "FOR", "FOREIGN", "FOUND", "FROM", "FULL", "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GROUP", "HAVING", "HOUR", "IDENTITY", "IMMEDIATE", "IN", "INDEX", "INDICATOR", "INITIALLY", "INNER", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL", "INTO", "IS", "ISOLATION", "JOIN", "KEY", "LANGUAGE", "LAST", "LEADING", "LEFT", "LEVEL", "LIKE", "LIMIT", "LIST", "LOCAL", "LOWER", "MATCH", "MAX", "MIN", "MINUTE", "MISSING", "MODULE", "MONTH", "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NEXT", "NO", "NOT", "NULL", "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER", "OUTER", "OUTPUT", "OVERLAPS", "PAD", "PARTIAL", "PIVOT", "POSITION", "PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", "READ", "REAL", "REFERENCES", "RELATIVE", "REMOVE", "RESTRICT", "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA", "SCROLL", "SECOND", "SECTION", "SELECT", "SESSION", "SESSION_USER", "SET", "SEXP", "SIZE", "SMALLINT", "SOME", "SPACE", "SQL", "SQLCODE", "SQLERROR", "SQLSTATE", "STRING", "STRUCT", "SUBSTRING", "SUM", "SYMBOL", "SYSTEM_USER", "TABLE", "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TO_STRING", "TO_TIMESTAMP", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", "TRIM", "TRUE", "TUPLE", "TXID", "UNDROP", "UNION", "UNIQUE", "UNKNOWN", "UNPIVOT", "UPDATE", "UPPER", "USAGE", "USER", "USING", "UTCNOW", "VALUE", "VALUES", "VARCHAR", "VARYING", "VIEW", "WHEN", "WHENEVER", "WHERE", "WITH", "WORK", "WRITE", "YEAR", "ZONE"]; const partiQLReservedWordsCheck = partiQLReservedWords.includes(attributeName.toUpperCase()); if (partiQLReservedWordsCheck) { throw new Error(`Please check attributeName does not contain PartiQL reserved words: https://docs.aws.amazon.com/qldb/latest/developerguide/ql-reference.reserved.html`); } return true; } exports.validateAttributeNameConstrains = validateAttributeNameConstrains; /** * Checks if a string is in a correct ISO 8601 datetime format like `2019-06-05T00:00:00Z` * @param dateTimeISO A string containing a name of a document attribute. * @returns Returns true if string complies with attribute naming constrains and trows an error if otherwise. */ function validateStringAsISODateTime(dateTimeISO) { const dateTimeStringRegexTemplate = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/g; const nameStringRegexCheckResult = dateTimeStringRegexTemplate.test(dateTimeISO); if (!nameStringRegexCheckResult) { throw new Error(`Please check dateTimeISO is in ISO 8601 datetime format like 2019-06-05T00:00:00Z. Received: ${dateTimeISO}`); } return true; } exports.validateStringAsISODateTime = validateStringAsISODateTime; /** * Convert dom.Value to writer-based Uint8Array * @param domValue The Ion value tof dom.Value type. * @returns Uint8Array value of the attribute. The buffer will * be either a UTF-8 encoded buffer for Ion text or Ion binary. The buffer is * not well-defined until [[close]] is invoked. */ function toWriterBytes(domValue) { const writer = (0, ion_js_1.makeTextWriter)(); domValue.writeTo(writer); return writer.getBytes(); } exports.toWriterBytes = toWriterBytes; /** * Convert metadata as JSON to UTF-8 encoded buffer for Ion to make sure all properties converted correctly * @param metadataJSON The value of the QLDB-generated metadata object, converted to JSON. * @returns Uint8Array value of the attribute. The buffer will * be either a UTF-8 encoded buffer for Ion text or Ion binary. The buffer is * not well-defined until [[close]] is invoked. */ function metadataJSONtoIonUint8Array(metadataJSON) { const writer = (0, ion_js_1.makeTextWriter)(); const jsonObject = JSON.parse(JSON.stringify(metadataJSON)); writer.stepIn(ion_js_1.IonTypes.STRUCT); // step into a struct for (const key in jsonObject) { if (jsonObject.hasOwnProperty(key)) { writer.writeFieldName(key); switch (key) { case 'txTime': writer.writeTimestamp(jsonObject[key]); logger.debug(`Converting json to ion key ${key} TIMESTAMP value ${JSON.stringify(jsonObject[key])}`); break; case 'version': writer.writeInt(jsonObject[key]); logger.debug(`Converting json to ion key ${key} INT value ${JSON.stringify(jsonObject[key])}`); break; default: logger.debug(`Converting json to ion key ${key} value ${JSON.stringify(jsonObject[key])}`); writer.writeString(jsonObject[key]); break; } } } writer.stepOut(); // step out of the struct writer.close(); // close the writer return writer.getBytes(); } exports.metadataJSONtoIonUint8Array = metadataJSONtoIonUint8Array;