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
JavaScript
;
/*
* 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;