UNPKG

larvitorder

Version:
242 lines 10.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Helpers = void 0; const uuidLib = __importStar(require("uuid")); const topLogPrefix = 'larvitorder: helpers.ts:'; class Helpers { constructor(options) { this.cachedOrderFields = []; this.cachedRowFields = []; if (!options.db) throw new Error('Missing required option "db"'); if (!options.log) throw new Error('Missing required option "log"'); if (!options.lUtils) throw new Error('Missing required option "lUtils"'); this.db = options.db; this.log = options.log; this.lUtils = options.lUtils; } async getFieldValues(options) { if (typeof options === 'string') { options = { fieldName: options }; } const dbFields = []; let sql = 'SELECT DISTINCT fieldValue\n'; sql += 'FROM orders_orders_fields\n'; sql += 'WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?)\n'; dbFields.push(options.fieldName); if (options.matchAllFields) { for (const fieldName in options.matchAllFields) { dbFields.push(fieldName); sql += 'AND orderUuid IN (\n'; sql += 'SELECT orderUuid\n'; sql += 'FROM orders_orders_fields\n'; if (Array.isArray(options.matchAllFields[fieldName])) { sql += 'WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue IN ('; for (let i = 0; i < options.matchAllFields[fieldName].length; i++) { dbFields.push(options.matchAllFields[fieldName][i]); sql += '?,'; } sql = sql.substring(0, sql.length - 1); sql += ')\n'; } else { dbFields.push(options.matchAllFields[fieldName]); sql += 'WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue = ?\n'; } sql += ')'; } } sql += 'ORDER BY fieldValue;'; const { rows } = await this.db.query(sql, dbFields); const names = rows.map((r) => r.fieldValue); return names; } async getOrderFieldUuid(fieldName) { const cachedOrderField = this.cachedOrderFields.find(field => field.name === fieldName); if (cachedOrderField) return cachedOrderField.uuid; // If we get down here, the field does not exist, create it and rerun const uuid = uuidLib.v1(); // NOTE: uuid is inserted as string here, it will be trunkated. Keep it as is for now to not break existing stuff. await this.db.query('INSERT IGNORE INTO orders_orderFields (uuid, name) VALUES(?,?)', [uuid, fieldName]); await this.loadOrderFieldsToCache(); return await this.getOrderFieldUuid(fieldName); } async getOrderFieldUuids(fieldNames) { const fieldUuidsByName = {}; for (const fieldName of fieldNames) { fieldUuidsByName[fieldName] = await this.getOrderFieldUuid(fieldName); } return fieldUuidsByName; } async getRowFieldUuid(rowFieldName) { const logPrefix = `${topLogPrefix} getRowFieldUuid() -`; if (rowFieldName === 'uuid') { const err = new Error('Row field "uuid" is reserved and have no uuid'); this.log.warn(`${logPrefix} ${err.message}`); throw err; } const cachedField = this.cachedRowFields.find(field => field.name === rowFieldName); if (cachedField) return cachedField.uuid; // If we get down here, the field does not exist, create it and rerun const uuid = uuidLib.v1(); // NOTE: uuid is inserted as string here, it will be trunkated. Keep it as is for now to not break existing stuff. await this.db.query('INSERT IGNORE INTO orders_rowFields (uuid, name) VALUES(?,?)', [uuid, rowFieldName]); await this.loadRowFieldsToCache(); return await this.getRowFieldUuid(rowFieldName); } async getRowFieldUuids(rowFieldNames) { const rowFieldUuidsByName = {}; for (const rowFieldName of rowFieldNames) { if (rowFieldName === 'uuid') continue; // Ignore uuid const fieldUuid = await this.getRowFieldUuid(rowFieldName); rowFieldUuidsByName[rowFieldName] = fieldUuid; } return rowFieldUuidsByName; } async loadOrderFieldsToCache() { const { rows } = await this.db.query('SELECT * FROM orders_orderFields ORDER BY name;'); this.cachedOrderFields = rows; } async loadRowFieldsToCache() { const { rows } = await this.db.query('SELECT * FROM orders_rowFields ORDER BY name;'); this.cachedRowFields = rows; } isBufferEqual(b1, b2) { if (b1.length !== b2.length) return false; for (let i = 0; i < b1.length; i++) { if (b1[i] !== b2[i]) return false; } return true; } formatUuid(uuid) { const uuidStr = this.lUtils.formatUuid(uuid); if (typeof uuidStr === 'boolean') throw new Error(`Failed to format uuid: "${uuid.toString()}"`); return uuidStr; } uuidToBuffer(uuid) { const uuidBuf = this.lUtils.uuidToBuffer(uuid); if (typeof uuidBuf === 'boolean') throw new Error(`Failed to convert uuid to buffer, uuid: "${uuid}"`); return uuidBuf; } arrayify(value) { if (value === undefined) return undefined; return Array.isArray(value) ? value : [value]; } // This is only giving true for positive (including 0) integers, keps this way for backwards compatibility isNumberIsh(value) { return typeof value === 'number' && (value % 1) === 0; } isDateIsh(value) { // value is a string representation of a date and time (e.g. "2021-01-01 00:00:00") // This function checks if the string is a valid date and time return !isNaN(Date.parse(value)); } async getChangedRows(dbCon, orderUuidBuf, orderRows, rowFieldUuidsByName) { // Get order rows const { rows: dbRows } = await dbCon.query('SELECT rowUuid FROM orders_rows WHERE orderUuid = ?', [orderUuidBuf]); // Get order row data const orderRowDataQuery = 'SELECT \n' + 'rowUuid, \n' + 'rowFieldUuid, \n' + 'rowIntValue, \n' + 'rowStrValue\n' + 'FROM orders_rows_fields \n' + 'WHERE rowUuid IN ( \n' + 'SELECT rowUuid FROM orders_rows WHERE orderUuid = ? \n' + ')'; const { rows: dbOrderRowData } = await dbCon.query(orderRowDataQuery, [orderUuidBuf]); // Compare data and remove untouched rows const changedRows = []; const removeRows = []; for (const dbRowUuidBuff of dbRows.map((x) => x.rowUuid)) { const dbRowUuid = this.formatUuid(dbRowUuidBuff); if (!orderRows.map(x => x.uuid).includes(dbRowUuid)) { removeRows.push({ rowUuid: dbRowUuid, rowUuidBuff: dbRowUuidBuff }); } } let rowAdded = false; for (const row of orderRows) { if (!row.uuid) { throw new Error('Row is missing uuid, make sure it has been set before calling getChangedRows()'); } const rowUuidBuff = this.uuidToBuffer(row.uuid); const foundDbRows = dbOrderRowData.filter((x) => this.isBufferEqual(x.rowUuid, rowUuidBuff)); if (!foundDbRows.length) { // New row. changedRows.push({ rowUuid: row.uuid, rowUuidBuff: rowUuidBuff, row: row }); rowAdded = true; continue; } rowAdded = false; for (const rowFieldName of Object.keys(row)) { if (rowAdded) break; if (rowFieldName === 'uuid') continue; const foundRowsByField = foundDbRows.filter((x) => this.isBufferEqual(x.rowFieldUuid, rowFieldUuidsByName[rowFieldName])); if (!foundRowsByField.length) { // New row. changedRows.push({ rowUuid: row.uuid, rowUuidBuff: rowUuidBuff, row: row }); rowAdded = true; break; } row[rowFieldName] = this.arrayify(row[rowFieldName]) ?? []; for (const rowFieldValue of row[rowFieldName]) { if (rowAdded) break; let intValue; let strValue; if (this.isNumberIsh(rowFieldValue)) { intValue = rowFieldValue; } else { strValue = String(rowFieldValue); } if (!foundRowsByField.find((x) => x.rowIntValue === (intValue !== undefined ? intValue : null) && x.rowStrValue === (strValue !== undefined ? strValue : null))) { // Changed row. changedRows.push({ rowUuid: row.uuid, rowUuidBuff: rowUuidBuff, row: row }); rowAdded = true; break; } } } } return { changedRows, removeRows }; } } exports.Helpers = Helpers; //# sourceMappingURL=helpers.js.map