@vulcan-sql/serve
Version:
VulcanSQL package for serving projects
94 lines • 4.34 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.CsvFormatter = exports.arrStringToCsvString = void 0;
const tslib_1 = require("tslib");
const Stream = require("stream");
const core_1 = require("@vulcan-sql/core");
const lodash_1 = require("lodash");
const responseFormatter_1 = require("../../models/extensions/responseFormatter");
const logger = (0, core_1.getLogger)({ scopeName: 'SERVE' });
/**
* convert the array string to one line string for csv format
* @param arrString
*/
const arrStringToCsvString = (arrString) => {
return arrString.replace(/^\[/, '').replace(/\\"/g, '""').replace(/\]$/, '');
};
exports.arrStringToCsvString = arrStringToCsvString;
class CsvTransformer extends Stream.Transform {
constructor({ columns, options, }) {
/**
* make the csv stream source (writable stream) is object mode to get data row directly from data readable stream.
* make the csv stream transformed destination (readable stream) is not object mode
*/
options = options || {
writableObjectMode: true,
readableObjectMode: false,
};
if ((0, lodash_1.isUndefined)(options.readableObjectMode))
options.readableObjectMode = false;
if ((0, lodash_1.isUndefined)(options.writableObjectMode))
options.writableObjectMode = true;
super(options);
this.PREPEND_UTF8_BOM = '\ufeff';
this.columns = columns;
/**
* add columns name by comma through join for csv title.
* in order to avoid the non-alphabet characters transform wrong, add PREPEND_UTF8_BOM prefix
*/
this.push((0, responseFormatter_1.toBuffer)(this.PREPEND_UTF8_BOM));
this.push((0, responseFormatter_1.toBuffer)(columns.join()));
this.push((0, responseFormatter_1.toBuffer)('\n'));
}
_transform(chunk, _encoding, callback) {
// chuck => { name: 'jack', age: 18, hobby:['book', 'travel'] }
// pick value and join it by semicolon, e.g: "\"jack\",18,\"['book', 'travel']\""
const valuesRow = this.columns.map((column) =>
// if value is array or object, stringify to fix in one column, e.g: ['book', 'travel'] => "['book', 'travel']"
(0, lodash_1.isObject)(chunk[column]) || (0, lodash_1.isArray)(chunk[column])
? JSON.stringify(chunk[column])
: chunk[column]);
// transform format data to buffer
const dataBuffer = (0, responseFormatter_1.toBuffer)((0, exports.arrStringToCsvString)(JSON.stringify(valuesRow)));
// run callback and pass the transformed data buffer to transform.push()
this.push(dataBuffer);
this.push((0, responseFormatter_1.toBuffer)('\n'));
callback(null);
}
}
let CsvFormatter = class CsvFormatter extends responseFormatter_1.BaseResponseFormatter {
format(data, columns) {
if (!columns)
throw new core_1.InternalError('must provide columns');
// create csv transform stream and define transform to csv way.
const csvStream = new CsvTransformer({
columns: columns.map((column) => column.name),
});
// start to transform data to csv stream
data
.pipe(csvStream)
.on('error', (err) => {
logger.warn(`read stream failed, detail error ${err}`);
throw new core_1.InternalError(`read data in the stream for formatting to csv failed.`);
})
.on('end', () => {
logger.debug('convert to csv format stream > done.');
});
return csvStream;
}
toResponse(stream, ctx) {
// get file name by url path. e.g: url = '/urls/orders', result = orders
const size = ctx.url.split('/').length;
const filename = ctx.url.split('/')[size - 1];
// set csv stream to response and header to note the stream will download
ctx.response.body = stream;
ctx.response.set('Content-disposition', `attachment; filename=${filename}.csv`);
ctx.response.set('Content-type', 'text/csv');
}
};
CsvFormatter = tslib_1.__decorate([
(0, core_1.VulcanInternalExtension)(),
(0, core_1.VulcanExtensionId)('csv')
], CsvFormatter);
exports.CsvFormatter = CsvFormatter;
//# sourceMappingURL=csvFormatter.js.map
;