UNPKG

qb-table

Version:

A lightweight abstraction layer for Quick Base

924 lines 34 kB
/*! * Package Name: qb-table * Package Description: A lightweight abstraction layer for Quick Base * Version: 5.0.23 * Build Timestamp: 2023-11-15T14:41:57.481Z * Package Homepage: https://github.com/tflanagan/node-qb-table * Git Location: git@github.com:tflanagan/node-qb-table.git * Authored By: Tristian Flanagan <contact@tristianflanagan.com> (https://github.com/tflanagan) * License: Apache-2.0 * * Copyright 2016 Tristian Flanagan * * 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. */ 'use strict'; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.QBTable = void 0; /* Dependencies */ const deepmerge_1 = __importDefault(require("deepmerge")); const rfc4122_1 = __importDefault(require("rfc4122")); const quickbase_1 = require("quickbase"); const qb_field_1 = require("qb-field"); const qb_record_1 = require("qb-record"); const qb_report_1 = require("qb-report"); /* Globals */ const VERSION = require('../package.json').version; const IS_BROWSER = typeof (window) !== 'undefined'; const rfc4122 = new rfc4122_1.default(); /* Main Class */ class QBTable { constructor(options) { this.CLASS_NAME = 'QBTable'; this._appId = ''; this._tableId = ''; this._fids = {}; this._fields = []; this._records = []; this._reports = []; this._data = {}; this.id = rfc4122.v4(); const _a = options || {}, { quickbase } = _a, classOptions = __rest(_a, ["quickbase"]); const settings = (0, deepmerge_1.default)(QBTable.defaults, classOptions); this.setAppId(settings.appId) .setTableId(settings.tableId) .setFids(settings.fids); if (quickbase_1.QuickBase.IsQuickBase(quickbase)) { this._qb = quickbase; } else { this._qb = new quickbase_1.QuickBase(deepmerge_1.default.all([ QBTable.defaults.quickbase, quickbase || {}, { tempTokenDbid: this.getTableId() } ])); } return this; } clear() { this._fields = []; this._records = []; this._reports = []; this._data = { id: '', alias: '', created: 0, updated: 0, name: '', description: '', singleRecordName: '', pluralRecordName: '', timeZone: '', dateFormat: 'MM-DD-YYYY', keyFieldId: 0, nextFieldId: 0, nextRecordId: 0, defaultSortFieldId: 0, defaultSortOrder: '' }; return this; } delete({ requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const results = yield this._qb.deleteTable({ appId: this.getAppId(), tableId: this.getTableId(), requestOptions }); this.setTableId(''); this.clear(); return results; }); } deleteRecord({ record, requestOptions }) { return __awaiter(this, void 0, void 0, function* () { let i = -1; this.getRecords().some((r, o) => { if (record.id === r.id || (record.get('recordid') && record.get('recordid') === r.get('recordid'))) { i = o; return true; } return false; }); let results = { numberDeleted: 1 }; if (i !== -1) { this._records.splice(i, 1); } if (record.get('recordid')) { results = yield record.delete({ requestOptions }); if (results.numberDeleted === 0) { this._records.push(record); } } return results; }); } deleteRecords({ individually = false, records, requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const results = { numberDeleted: 0 }; if (individually) { if (records === undefined) { records = this.getRecords(); } for (const record of records) { const result = yield this.deleteRecord({ record, requestOptions }); results.numberDeleted += result.numberDeleted; } } else { if (records === undefined) { records = this._records.splice(0, this._records.length); } const batches = records.reduce((batches, record) => { if (record.get('recordid') <= 0) { return batches; } if (batches[batches.length - 1].length === 100) { batches.push([]); } batches[batches.length - 1].push(record); return batches; }, [[]]); for (let i = 0; i < batches.length; ++i) { const result = yield this._qb.deleteRecords({ tableId: this.getTableId(), where: batches[i].map((record) => { return `{'${this.getFid('recordid')}'.EX.'${record.get('recordid')}'}`; }).join('AND'), requestOptions }); results.numberDeleted += result.numberDeleted; } } return results; }); } get(attribute) { if (attribute === 'id' || attribute === 'tableId') { return this.getTableId(); } else if (attribute === 'appId') { return this.getAppId(); } return this._data[attribute]; } getAppId() { return this._appId; } getFid(field, byId = false) { const fids = this.getFids(); let id = -1; if (byId !== true) { if (fids.hasOwnProperty(field)) { id = fids[field]; } } else { id = ''; field = +field; Object.keys(fids).some((name) => { if (fids[name] === field) { id = name; return true; } return false; }); } return id; } getFids() { return this._fids; } getField(id, returnIndex = false) { const fields = this.getFields(); let result = undefined; for (let i = 0; result === undefined && i < fields.length; ++i) { if (fields[i].getFid() === id) { result = returnIndex ? i : fields[i]; } } return result; } getFields() { return this._fields; } getNRecords() { return this._records.length; } getRecord(value, fieldName = 'recordid', returnIndex = false) { const records = this.getRecords(); let i = -1; records.some((record, o) => { if (record.get(fieldName) !== value) { return false; } i = o; return true; }); if (returnIndex) { return i; } else if (i === -1) { return undefined; } return records[i]; } getRecords() { return this._records; } getReport(id) { let result; for (let i = 0; !result && i < this._reports.length; ++i) { if (this._reports[i].getReportId() === id) { result = this._reports[i]; } } return result; } getReports() { return this._reports; } getTableId() { return this._tableId; } getTempToken({ requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { yield this._qb.getTempTokenDBID({ dbid: this.getTableId(), requestOptions }); }); } loadField({ field, requestOptions }) { return __awaiter(this, void 0, void 0, function* () { if (!qb_field_1.QBField.IsQBField(field)) { field = this.getField(field) || field; if (!qb_field_1.QBField.IsQBField(field)) { field = new qb_field_1.QBField({ quickbase: this._qb, tableId: this.getTableId(), fid: field }); this._fields.push(field); } } yield field.load({ requestOptions }); return field; }); } loadFields({ requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const results = yield this._qb.getFields({ tableId: this.getTableId(), requestOptions }); results.forEach((field) => { let result = this.getField(field.id); if (!result) { result = new qb_field_1.QBField({ quickbase: this._qb, tableId: this.getTableId(), fid: field.id }); this._fields.push(result); } Object.entries(field).forEach(([attribute, value]) => { result.set(attribute, value); }); }); return this.getFields(); }); } loadReport({ report, requestOptions }) { return __awaiter(this, void 0, void 0, function* () { if (!qb_report_1.QBReport.IsQBReport(report)) { report = this.getReport(report) || report; if (!qb_report_1.QBReport.IsQBReport(report)) { report = new qb_report_1.QBReport({ quickbase: this._qb, tableId: this.getTableId(), reportId: report }); report.setFids(this.getFids()); this._reports.push(report); } } yield report.load({ requestOptions }); return report; }); } loadReports({ requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const results = yield this._qb.getTableReports({ tableId: this.getTableId(), requestOptions }); this._reports = results.map((report) => { const qbReport = new qb_report_1.QBReport({ quickbase: this._qb, tableId: this.getTableId(), reportId: report.id }); Object.entries(report).forEach(([key, value]) => { qbReport.set(key, value); }); qbReport.setFids(this.getFids()); return qbReport; }); return this._reports; }); } loadSchema({ requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const results = yield Promise.all([ this.loadFields({ requestOptions }), this.loadReports({ requestOptions }), this.loadTable({ requestOptions }) ]); return Object.assign(Object.assign({}, results[2]), { reports: results[1], fields: results[0] }); }); } loadTable({ requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const results = yield this._qb.getTable({ appId: this.getAppId(), tableId: this.getTableId(), requestOptions }); Object.entries(results).forEach(([attribute, value]) => { this.set(attribute, value); }); return this._data; }); } _runQueryAll(query) { var _a; return __awaiter(this, void 0, void 0, function* () { const results = { metadata: { numFields: 0, numRecords: 0, skip: 0, top: 0, totalRecords: 0 }, data: [], fields: [] }; let firstRun = true, skip = ((_a = query.options) === null || _a === void 0 ? void 0 : _a.skip) || 0, top = 0, total = 0; while (true) { const batchQuery = Object.assign(Object.assign({}, query), { sortBy: [ ...(query.sortBy || []), { fieldId: 3, order: 'ASC' } ] }); if (!firstRun) { batchQuery.options = { skip, top }; } if (!this._qb.settings.userToken) { yield this._qb.getTempTokenDBID({ dbid: batchQuery.tableId }); } const { fields, metadata, data } = yield this._qb.runQuery(Object.assign(Object.assign({}, batchQuery), { returnAxios: false })); if (firstRun) { results.fields = fields; results.metadata = metadata; results.data = data; total = metadata.totalRecords; firstRun = false; } else { results.data = results.data.concat(data); } top = metadata.top || metadata.numRecords || top; // top doesn't always return in metadata skip += metadata.numRecords; if (skip >= total) { break; } } results.metadata.skip = 0; results.metadata.top = results.data.length; results.metadata.numRecords = results.data.length; results.metadata.totalRecords = results.data.length; return results; }); } runQuery({ fids, groupBy, options, select, sortBy, where, returnAll, requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { if (!fids) { fids = this.getFids(); } const names = Object.keys(fids); const selectedFids = names.reduce((selectedFids, name) => { const fid = fids[name]; if (!select || select.length === 0 || select.indexOf(fid) !== -1) { selectedFids[name] = fid; } return selectedFids; }, {}); const selectedNames = Object.keys(selectedFids); const query = { tableId: this.getTableId(), select: selectedNames.map((name) => { return selectedFids[name]; }).filter(filterUnique), where: where, requestOptions, returnAxios: false }; if (sortBy) { query.sortBy = sortBy; } if (groupBy) { query.groupBy = groupBy; } if (options) { query.options = options; } let results; if (returnAll) { results = yield this._runQueryAll(query); } else { results = yield this._qb.runQuery(query); } results.fields.forEach((field) => { let result = this.getField(field.id); if (!result) { result = new qb_field_1.QBField({ quickbase: this._qb, tableId: this.getTableId(), fid: field.id }); this._fields.push(result); } Object.entries(field).forEach(([attribute, value]) => { result.set(attribute, value); }); }); const fields = this.getFields(); this._records = results.data.map((record) => { const qbRecord = new qb_record_1.QBRecord({ quickbase: this._qb, tableId: this.getTableId(), fids: this.getFids() }); qbRecord.setFields(fields); selectedNames.forEach((name) => { const fid = selectedFids[name]; qbRecord.set('' + (name || fid), record[fid] ? record[fid].value : undefined); }); return qbRecord; }); return { metadata: results.metadata, fields: this.getFields(), records: this.getRecords() }; }); } runReport({ report, skip, top, requestOptions }) { return __awaiter(this, void 0, void 0, function* () { if (!qb_report_1.QBReport.IsQBReport(report)) { report = this.getReport(report) || report; if (!qb_report_1.QBReport.IsQBReport(report)) { report = new qb_report_1.QBReport({ quickbase: this._qb, tableId: this.getTableId(), reportId: report }); report.setFids(this.getFids()); this._reports.push(report); } } const results = yield report.run({ skip, top, requestOptions }); this._records = report.getRecords(); return results; }); } saveFields({ attributesToSave, requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const fields = this.getFields(); for (let i = 0; i < fields.length; ++i) { yield fields[i].save({ attributesToSave, requestOptions }); } return fields; }); } saveRecords({ individually, fidsToSave, recordsToSave, mergeFieldId, requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const records = recordsToSave === undefined ? this.getRecords() : recordsToSave; if (individually) { for (let i = 0; i < records.length; ++i) { yield records[i].save({ fidsToSave, requestOptions }); } } else { const mergeField = mergeFieldId || this.getFid('primaryKey'); const fids = this.getFids(); const names = Object.keys(fids); const selectedNames = names.filter((name) => { const fid = fids[name]; const filtered = !fidsToSave || fidsToSave.indexOf(fid) !== -1 || fidsToSave.indexOf(name) !== -1 || fid === mergeField; if (!filtered) { return false; } const field = this.getField(fid); if (field && [ 'lookup', 'summary', 'formula' ].indexOf(field.get('mode') || '') !== -1) { return false; } return true; }); const inputRecords = records.sort((a, b) => { const aVal = a.get('recordid'); const bVal = b.get('recordid'); if (aVal === bVal) { return 0; } else if (aVal === undefined && bVal !== undefined) { return -1; } else if (aVal !== undefined && bVal === undefined) { return 1; } return aVal > bVal ? 1 : -1; }); const { headers, data: results } = yield this._qb.upsert({ tableId: this.getTableId(), mergeFieldId: mergeField, data: inputRecords.map((qbRecord) => { return selectedNames.reduce((record, name) => { const fid = fids[name]; if (fid) { record[fid] = { value: (0, qb_record_1.replaceUndefinedWithString)(qbRecord.get(name)) }; } return record; }, {}); }), fieldsToReturn: names.map((name) => { return fids[name]; }).filter(filterUnique), requestOptions, returnAxios: true }); const errors = []; for (let inputI = 0, dataI = 0; inputI < inputRecords.length; ++inputI) { const record = inputRecords[inputI]; const lineNum = inputI + 1; const error = results.metadata.lineErrors && results.metadata.lineErrors[lineNum]; if (error) { errors.push(new quickbase_1.QuickBaseError(207, `Error on Line ${lineNum}`, error.join('\n'), headers['qb-api-ray'])); } else { const data = record.get('recordid') ? results.data.find((result) => { return result['3'].value === record.get('recordid'); }) : results.data[dataI]; if (data) { names.forEach((name) => { const fid = fids[name]; if (fid) { const field = data[fid]; if (field) { record.set(name, field.value); } } }); } ++dataI; } } if (errors.length > 0) { if (typeof (AggregateError) !== 'undefined') { throw new AggregateError(errors, 'A partial success response was returned'); } else { throw new quickbase_1.QuickBaseError(207, 'A partial success response was returned', errors.map((err) => { return `${err.message}: ${err.description}`; }).join('\n'), headers['qb-api-ray']); } } } return records; }); } saveTable({ attributesToSave, requestOptions } = {}) { return __awaiter(this, void 0, void 0, function* () { const tableId = this.getTableId(); const data = Object.keys(this._data).filter((attribute) => { return [ 'name', 'description', 'iconName', 'singularNoun', 'pluralNoun' ].indexOf(attribute) !== -1 && (!attributesToSave || attributesToSave.indexOf(attribute) === -1); }).reduce((results, attribute) => { results[attribute] = this._data[attribute]; return results; }, { appId: this.getAppId(), requestOptions }); let results; if (tableId) { data.tableId = tableId; results = yield this._qb.updateTable(data); } else { results = yield this._qb.createTable(data); } Object.entries(results).forEach(([attribute, value]) => { this.set(attribute, value); }); return this._data; }); } set(attribute, value) { if (attribute === 'id' || attribute === 'tableId') { return this.setTableId(value); } else if (attribute === 'appid') { return this.setAppId(value); } this._data[attribute] = value; return this; } setAppId(appId) { this._appId = appId; return this; } setFid(name, id) { this._fids[name] = +id; return this; } setFids(fields) { Object.entries(fields).forEach(([name, fid]) => { this.setFid(name, fid); }); return this; } setTableId(tableId) { this._tableId = tableId; this._data.id = tableId; return this; } upsertField(options, autoSave = false) { return __awaiter(this, void 0, void 0, function* () { let field; if (qb_field_1.QBField.IsQBField(options)) { if (options.get('recordid')) { field = this.getField(options.get('fid')); } else if (options.get('primaryKey')) { field = this.getField(options.get('fid')); } else { field = options; } } else if (options !== undefined) { if (options.fid) { field = this.getField(options.fid); } else if (options.id) { field = this.getField(options.id); } } if (!field) { field = new qb_field_1.QBField({ quickbase: this._qb, tableId: this.getTableId(), fid: -1 }); if (options && !qb_field_1.QBField.IsQBField(options) && options.fid) { field.setFid(options.fid); } this._fields.push(field); } if (options && !qb_field_1.QBField.IsQBField(options)) { Object.entries(options).forEach(([attribute, value]) => { field.set(attribute, value); }); } if (autoSave) { yield field.save(); } return field; }); } upsertFields(fields, autoSave = false) { return __awaiter(this, void 0, void 0, function* () { const results = []; for (let i = 0; i < fields.length; ++i) { results.push(yield this.upsertField(fields[i], autoSave)); } return results; }); } upsertRecord(options, autoSave = false) { return __awaiter(this, void 0, void 0, function* () { let record; if (qb_record_1.QBRecord.IsQBRecord(options)) { if (options.get('recordid')) { record = this.getRecord(options.get('recordid'), 'recordid'); } else if (options.get('primaryKey')) { record = this.getRecord(options.get('primaryKey'), 'primaryKey'); } else { record = options; } } else if (options !== undefined) { if (options.recordid) { record = this.getRecord(options.recordid, 'recordid'); } else if (options.primaryKey) { record = this.getRecord(options.primaryKey, 'primaryKey'); } } if (!record) { record = new qb_record_1.QBRecord({ quickbase: this._qb, tableId: this.getTableId(), fids: this.getFids() }); this._records.push(record); } record.setFields(this.getFields()); if (options && !qb_record_1.QBRecord.IsQBRecord(options)) { const addDefaults = !record.get('recordid'); Object.entries(options).forEach(([fidName, fidValue]) => { var _a; let value; if (addDefaults) { const fid = this.getFid(fidName); const field = this.getField(fid); if (field) { value = (_a = field.get('properties')) === null || _a === void 0 ? void 0 : _a.defaultValue; } if (fidValue !== undefined) { value = fidValue; } } else { value = fidValue; } record.set(fidName, value); }); } if (autoSave) { yield record.save(); } return record; }); } upsertRecords(records, autoSave = false) { return __awaiter(this, void 0, void 0, function* () { const results = []; for (let i = 0; i < records.length; ++i) { results.push(yield this.upsertRecord(records[i], autoSave)); } return results; }); } /** * Test if a variable is a `qb-record` object * * @param obj A variable you'd like to test */ static IsQBTable(obj) { return (obj || {}).CLASS_NAME === QBTable.CLASS_NAME; } static NewRecord(table, data) { return qb_record_1.QBRecord.NewRecord({ quickbase: table._qb, tableId: table.getTableId(), fids: table.getFids() }, data); } static ToCSV(table, columns, data) { return (data || table.getRecords()).map((record) => { return columns.map((field) => { const value = record.get(field); switch (typeof (value)) { case 'number': return value; case 'boolean': return `"${value ? 'yes' : 'no'}"`; case 'object': return `"${JSON.stringify(value).replace(/"/g, '""')}"`; default: return `"${value.replace(/"/g, '""')}"`; } }).join(','); }).join('\n'); } } exports.QBTable = QBTable; QBTable.CLASS_NAME = 'QBTable'; /** * The loaded library version */ QBTable.VERSION = VERSION; /** * The default settings of a `QuickBase` instance */ QBTable.defaults = { quickbase: { realm: IS_BROWSER ? window.location.host.split('.')[0] : '' }, appId: '', tableId: (() => { if (IS_BROWSER) { const tableId = window.location.pathname.match(/^\/db\/(?!main)(.*)$/); if (tableId) { return tableId[1]; } } return ''; })(), fids: { recordid: 3, primaryKey: 3 } }; /* Helpers */ const filterUnique = (val, i, arr) => { return arr.indexOf(val) === i; }; /* Export to Browser */ if (IS_BROWSER) { window.QBTable = exports; } //# sourceMappingURL=qb-table.js.map