qb-table
Version:
A lightweight abstraction layer for Quick Base
924 lines • 34 kB
JavaScript
/*!
* 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.
*/
;
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