@tableau/taco-toolkit
Version:
Tableau Connector Toolkit
367 lines • 23.2 kB
JavaScript
;
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 __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFileEmpty = exports.composeDataRowsWithTypeFromObjectArray = exports.isValidDataRows = exports.composeDataRowsWithType = exports.composeDataRows = void 0;
var fs_1 = __importDefault(require("fs"));
var column_header_property_1 = require("../../shared/enums/column-header-property");
/**
* Convert array type data into key-value pair DataRow array.
*
* The function uses array index to align data and column name.
*/
function composeDataRows(rows, columnNames) {
var e_1, _a;
var dataRows = [];
try {
for (var rows_1 = __values(rows), rows_1_1 = rows_1.next(); !rows_1_1.done; rows_1_1 = rows_1.next()) {
var row = rows_1_1.value;
var dataRow = {};
for (var index = 0; index < columnNames.length; index++) {
var columnName = columnNames[index];
dataRow[columnName] = row[index];
}
dataRows.push(dataRow);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (rows_1_1 && !rows_1_1.done && (_a = rows_1.return)) _a.call(rows_1);
}
finally { if (e_1) throw e_1.error; }
}
return dataRows;
}
exports.composeDataRows = composeDataRows;
/**
* Convert array type data into key-value pair DataRow array with column metadata.
* The function uses array index to align data and column name.
*
* The result will only contain the columns that exists in the columnMetadata.
* The data values will be converted into into corresponding type based on columnMetadata.
*
* - rows and columnNames determine the alignment and the order
* - columnMetadata determines the selected column and the data value type
*/
function composeDataRowsWithType(rows, columnNames, columnMetadata) {
var e_2, _a, e_3, _b;
var dataRows = [];
var filteredColumns = [];
columnNames.forEach(function (columnName, index) {
var columnHeader = columnMetadata.get(columnName);
if (columnHeader) {
filteredColumns.push({
index: index,
columnName: columnName,
columnHeader: columnHeader,
});
}
});
if (filteredColumns.length < columnMetadata.size) {
var missingColumns = Array.from(columnMetadata.keys()).filter(function (columnName) {
return !filteredColumns.find(function (columnHeader) { return columnHeader.columnName === columnName; });
});
throw new Error("'columnNames' is missing columns: ".concat(missingColumns));
}
try {
for (var rows_2 = __values(rows), rows_2_1 = rows_2.next(); !rows_2_1.done; rows_2_1 = rows_2.next()) {
var row = rows_2_1.value;
var dataRow = {};
try {
for (var filteredColumns_1 = (e_3 = void 0, __values(filteredColumns)), filteredColumns_1_1 = filteredColumns_1.next(); !filteredColumns_1_1.done; filteredColumns_1_1 = filteredColumns_1.next()) {
var _c = filteredColumns_1_1.value, index = _c.index, columnName = _c.columnName, columnHeader = _c.columnHeader;
var value = row[index];
dataRow[columnName] = getValueForType(value, columnHeader.dataType);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (filteredColumns_1_1 && !filteredColumns_1_1.done && (_b = filteredColumns_1.return)) _b.call(filteredColumns_1);
}
finally { if (e_3) throw e_3.error; }
}
dataRows.push(dataRow);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (rows_2_1 && !rows_2_1.done && (_a = rows_2.return)) _a.call(rows_2);
}
finally { if (e_2) throw e_2.error; }
}
return dataRows;
}
exports.composeDataRowsWithType = composeDataRowsWithType;
/**
* Determines if an array of records is a valid array of DataRow.
* We are assuming here that the rows object is non-empty.
*
* @param {Record<string, unknown>[]} rows - The array of records to check.
*
* @returns {boolean} - A boolean indicating whether the array is a valid array of data rows.
*/
function isValidDataRows(rows) {
var e_4, _a;
// Get the column names from the first row to use as a reference for subsequent rows.
var columnNames = Object.keys(rows[0]);
try {
// Check each row and each column value to make sure they are valid DataRow values.
for (var rows_3 = __values(rows), rows_3_1 = rows_3.next(); !rows_3_1.done; rows_3_1 = rows_3.next()) {
var row = rows_3_1.value;
for (var index = 0; index < columnNames.length; index++) {
var columnName = columnNames[index];
var rowValue = row[columnName];
if (!isDataRowValue(rowValue)) {
return false;
}
}
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (rows_3_1 && !rows_3_1.done && (_a = rows_3.return)) _a.call(rows_3);
}
finally { if (e_4) throw e_4.error; }
}
// If all rows and all column values pass the validity check, return true.
return true;
}
exports.isValidDataRows = isValidDataRows;
/**
* Determines if a given value is a valid value for a table row column.
*
* @param {unknown} value - The value to check.
*
* @returns {boolean} - A boolean indicating whether the value is a valid DataRow value.
*/
function isDataRowValue(value) {
// A valid DataRow value is either null or a non-object type.
return value === null || typeof value !== 'object';
}
/**
* Composes an array of data rows with typed values from an array of objects.
* We are assuming here that the rows object is non-empty.
*
* @param {UnparsedDataRow[]} rows - The array of objects to use as a source of values.
* @param {Map<string, ColumnHeader>} columnMetadata - A map of column headers that describes the expected types for each column.
*
* @returns {DataRow[]} - An array of data rows with typed values.
*
* @throws {Error} - If any columns are missing from the input array of objects.
*/
function composeDataRowsWithTypeFromObjectArray(rows, columnMetadata) {
var e_5, _a, e_6, _b;
// Initialize an empty array to store the composed data rows.
var dataRows = [];
// Get the column names from the first row to use as a reference for subsequent rows.
var columnNames = Object.keys(rows[0]);
// Filter the column names to only include those that have metadata in the columnMetadata map.
var filteredColumns = [];
columnNames.forEach(function (columnName, index) {
var columnHeader = columnMetadata.get(columnName);
if (columnHeader) {
filteredColumns.push({
index: index,
columnName: columnName,
columnHeader: columnHeader,
});
}
});
// Check if any columns are missing from the filtered column list and throw an error if so.
if (filteredColumns.length < columnMetadata.size) {
var missingColumns = Array.from(columnMetadata.keys()).filter(function (columnName) {
return !filteredColumns.find(function (columnHeader) { return columnHeader.columnName === columnName; });
});
throw new Error("'columnNames' is missing columns: ".concat(missingColumns));
}
try {
// Loop through each input object and compose a data row with typed values.
for (var rows_4 = __values(rows), rows_4_1 = rows_4.next(); !rows_4_1.done; rows_4_1 = rows_4.next()) {
var row = rows_4_1.value;
var dataRow = {};
try {
for (var filteredColumns_2 = (e_6 = void 0, __values(filteredColumns)), filteredColumns_2_1 = filteredColumns_2.next(); !filteredColumns_2_1.done; filteredColumns_2_1 = filteredColumns_2.next()) {
var _c = filteredColumns_2_1.value, columnName = _c.columnName, columnHeader = _c.columnHeader;
var value = row[columnName];
dataRow[columnName] = getValueForType(value, columnHeader.dataType);
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (filteredColumns_2_1 && !filteredColumns_2_1.done && (_b = filteredColumns_2.return)) _b.call(filteredColumns_2);
}
finally { if (e_6) throw e_6.error; }
}
dataRows.push(dataRow);
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (rows_4_1 && !rows_4_1.done && (_a = rows_4.return)) _a.call(rows_4);
}
finally { if (e_5) throw e_5.error; }
}
return dataRows;
}
exports.composeDataRowsWithTypeFromObjectArray = composeDataRowsWithTypeFromObjectArray;
/**
* Using a read stream is likely the most performant method for very large files,
* since it reads only the first chunk of data from the file and does not retrieve the file size.
*/
function isFileEmpty(filepath) {
var _a, e_7, _b, _c;
return __awaiter(this, void 0, void 0, function () {
var stream, _d, stream_1, stream_1_1, chunk, e_7_1;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
stream = fs_1.default.createReadStream(filepath);
_e.label = 1;
case 1:
_e.trys.push([1, , 14, 15]);
_e.label = 2;
case 2:
_e.trys.push([2, 7, 8, 13]);
_d = true, stream_1 = __asyncValues(stream);
_e.label = 3;
case 3: return [4 /*yield*/, stream_1.next()];
case 4:
if (!(stream_1_1 = _e.sent(), _a = stream_1_1.done, !_a)) return [3 /*break*/, 6];
_c = stream_1_1.value;
_d = false;
try {
chunk = _c;
if (chunk.length > 0) {
return [2 /*return*/, false];
}
}
finally {
_d = true;
}
_e.label = 5;
case 5: return [3 /*break*/, 3];
case 6: return [3 /*break*/, 13];
case 7:
e_7_1 = _e.sent();
e_7 = { error: e_7_1 };
return [3 /*break*/, 13];
case 8:
_e.trys.push([8, , 11, 12]);
if (!(!_d && !_a && (_b = stream_1.return))) return [3 /*break*/, 10];
return [4 /*yield*/, _b.call(stream_1)];
case 9:
_e.sent();
_e.label = 10;
case 10: return [3 /*break*/, 12];
case 11:
if (e_7) throw e_7.error;
return [7 /*endfinally*/];
case 12: return [7 /*endfinally*/];
case 13: return [2 /*return*/, true];
case 14:
stream.close();
return [7 /*endfinally*/];
case 15: return [2 /*return*/];
}
});
});
}
exports.isFileEmpty = isFileEmpty;
function getValueForType(value, type) {
switch (type) {
case column_header_property_1.DataType.Date:
case column_header_property_1.DataType.Datetime: {
if (!value) {
return null;
}
return value;
}
case column_header_property_1.DataType.Int:
case column_header_property_1.DataType.Float: {
if (value === '' || value === undefined || value === null) {
return null;
}
return Number(value);
}
case column_header_property_1.DataType.Bool: {
if (value === '' || value === undefined || value === null) {
return null;
}
return value.toLowerCase() === 'true';
}
case column_header_property_1.DataType.Geometry: {
if (!value) {
return null;
}
return JSON.parse(value);
}
default: {
if (value === undefined) {
return null;
}
return value;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2UtdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXBzL3V0aWxzL3BhcnNlLXV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDBDQUFtQjtBQUVuQixvRkFBb0U7QUFNcEU7Ozs7R0FJRztBQUNILFNBQWdCLGVBQWUsQ0FBQyxJQUFnQixFQUFFLFdBQXFCOztJQUNyRSxJQUFNLFFBQVEsR0FBYyxFQUFFLENBQUE7O1FBQzlCLEtBQWtCLElBQUEsU0FBQSxTQUFBLElBQUksQ0FBQSwwQkFBQSw0Q0FBRTtZQUFuQixJQUFNLEdBQUcsaUJBQUE7WUFDWixJQUFNLE9BQU8sR0FBWSxFQUFFLENBQUE7WUFDM0IsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLFdBQVcsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUU7Z0JBQ3ZELElBQU0sVUFBVSxHQUFHLFdBQVcsQ0FBQyxLQUFLLENBQUMsQ0FBQTtnQkFDckMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQTthQUNqQztZQUNELFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7U0FDdkI7Ozs7Ozs7OztJQUNELE9BQU8sUUFBUSxDQUFBO0FBQ2pCLENBQUM7QUFYRCwwQ0FXQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLHVCQUF1QixDQUNyQyxJQUFnQixFQUNoQixXQUFxQixFQUNyQixjQUF5Qzs7SUFFekMsSUFBTSxRQUFRLEdBQWMsRUFBRSxDQUFBO0lBRTlCLElBQU0sZUFBZSxHQUF3RSxFQUFFLENBQUE7SUFDL0YsV0FBVyxDQUFDLE9BQU8sQ0FBQyxVQUFDLFVBQVUsRUFBRSxLQUFLO1FBQ3BDLElBQU0sWUFBWSxHQUFHLGNBQWMsQ0FBQyxH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