mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) updates from grist-core
This commit is contained in:
@@ -85,7 +85,7 @@ import {AccessTokenOptions, AccessTokenResult, GristDocAPI, UIRowId} from 'app/p
|
||||
import {compileAclFormula} from 'app/server/lib/ACLFormula';
|
||||
import {AssistanceSchemaPromptV1Context} from 'app/server/lib/Assistance';
|
||||
import {AssistanceContext} from 'app/common/AssistancePrompts';
|
||||
import {Authorizer} from 'app/server/lib/Authorizer';
|
||||
import {Authorizer, RequestWithLogin} from 'app/server/lib/Authorizer';
|
||||
import {checksumFile} from 'app/server/lib/checksumFile';
|
||||
import {Client} from 'app/server/lib/Client';
|
||||
import {DEFAULT_CACHE_TTL, DocManager} from 'app/server/lib/DocManager';
|
||||
@@ -141,6 +141,7 @@ import remove = require('lodash/remove');
|
||||
import sum = require('lodash/sum');
|
||||
import without = require('lodash/without');
|
||||
import zipObject = require('lodash/zipObject');
|
||||
import { getMetaTables } from './DocApi';
|
||||
|
||||
bluebird.promisifyAll(tmp);
|
||||
|
||||
@@ -2791,7 +2792,6 @@ export function tableIdToRef(metaTables: { [p: string]: TableDataAction }, table
|
||||
|
||||
// Helper that converts a Grist column colId to a ref given the corresponding table.
|
||||
export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId: string, colId: string) {
|
||||
|
||||
const tableRef = tableIdToRef(metaTables, tableId);
|
||||
|
||||
const [, , colRefs, columnData] = metaTables._grist_Tables_column;
|
||||
@@ -2804,6 +2804,31 @@ export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId:
|
||||
return colRefs[colRowIndex];
|
||||
}
|
||||
|
||||
// Helper that check if tableRef is used instead of tableId and return real tableId
|
||||
// If metaTables is not define, activeDoc and req allow it to be created
|
||||
interface MetaTables {
|
||||
metaTables: { [p: string]: TableDataAction }
|
||||
}
|
||||
interface ActiveDocAndReq {
|
||||
activeDoc: ActiveDoc, req: RequestWithLogin
|
||||
}
|
||||
export async function getRealTableId(
|
||||
tableId: string,
|
||||
options: MetaTables | ActiveDocAndReq
|
||||
): Promise<string> {
|
||||
if (parseInt(tableId)) {
|
||||
const metaTables = "metaTables" in options
|
||||
? options.metaTables
|
||||
: await getMetaTables(options.activeDoc, options.req);
|
||||
const [, , tableRefs, tableData] = metaTables._grist_Tables;
|
||||
if (tableRefs.indexOf(parseInt(tableId)) >= 0) {
|
||||
const tableRowIndex = tableRefs.indexOf(parseInt(tableId));
|
||||
return tableData.tableId[tableRowIndex]!.toString();
|
||||
}
|
||||
}
|
||||
return tableId;
|
||||
}
|
||||
|
||||
export function sanitizeApplyUAOptions(options?: ApplyUAOptions): ApplyUAOptions {
|
||||
return pick(options||{}, ['desc', 'otherId', 'linkId', 'parseStrings']);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
TableOperationsImpl,
|
||||
TableOperationsPlatform
|
||||
} from 'app/plugin/TableOperationsImpl';
|
||||
import {ActiveDoc, colIdToRef as colIdToReference, tableIdToRef} from "app/server/lib/ActiveDoc";
|
||||
import {ActiveDoc, colIdToRef as colIdToReference, getRealTableId, tableIdToRef} from "app/server/lib/ActiveDoc";
|
||||
import {appSettings} from "app/server/lib/AppSettings";
|
||||
import {sendForCompletion} from 'app/server/lib/Assistance';
|
||||
import {
|
||||
@@ -201,7 +201,7 @@ export class DocWorkerApi {
|
||||
if (!Object.keys(filters).every(col => Array.isArray(filters[col]))) {
|
||||
throw new ApiError("Invalid query: filter values must be arrays", 400);
|
||||
}
|
||||
const tableId = optTableId || req.params.tableId;
|
||||
const tableId = await getRealTableId(optTableId || req.params.tableId, {activeDoc, req});
|
||||
const session = docSessionFromRequest(req);
|
||||
const {tableData} = await handleSandboxError(tableId, [], activeDoc.fetchQuery(
|
||||
session, {tableId, filters}, !immediate));
|
||||
@@ -262,11 +262,6 @@ export class DocWorkerApi {
|
||||
})
|
||||
);
|
||||
|
||||
async function getMetaTables(activeDoc: ActiveDoc, req: RequestWithLogin) {
|
||||
return await handleSandboxError("", [],
|
||||
activeDoc.fetchMetaTables(docSessionFromRequest(req)));
|
||||
}
|
||||
|
||||
const registerWebhook = async (activeDoc: ActiveDoc, req: RequestWithLogin, webhook: WebhookFields) => {
|
||||
const {fields, url} = await getWebhookSettings(activeDoc, req, null, webhook);
|
||||
if (!fields.eventTypes?.length) {
|
||||
@@ -337,7 +332,8 @@ export class DocWorkerApi {
|
||||
const trigger = webhookId ? activeDoc.triggers.getWebhookTriggerRecord(webhookId) : undefined;
|
||||
let currentTableId = trigger ? tablesTable.getValue(trigger.tableRef, 'tableId')! : undefined;
|
||||
const {url, eventTypes, isReadyColumn, name} = webhook;
|
||||
const tableId = req.params.tableId || webhook.tableId;
|
||||
const tableId = await getRealTableId(req.params.tableId || webhook.tableId, {metaTables});
|
||||
|
||||
const fields: Partial<SchemaTypes['_grist_Triggers']> = {};
|
||||
|
||||
if (url && !isUrlAllowed(url)) {
|
||||
@@ -387,7 +383,7 @@ export class DocWorkerApi {
|
||||
// Get the columns of the specified table in recordish format
|
||||
this._app.get('/api/docs/:docId/tables/:tableId/columns', canView,
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const tableId = req.params.tableId;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const includeHidden = isAffirmative(req.query.hidden);
|
||||
const columns = await handleSandboxError('', [],
|
||||
activeDoc.getTableCols(docSessionFromRequest(req), tableId, includeHidden));
|
||||
@@ -498,7 +494,7 @@ export class DocWorkerApi {
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const colValues = req.body as BulkColValues;
|
||||
const count = colValues[Object.keys(colValues)[0]].length;
|
||||
const op = getTableOperations(req, activeDoc);
|
||||
const op = await getTableOperations(req, activeDoc);
|
||||
const ids = await op.addRecords(count, colValues);
|
||||
res.json(ids);
|
||||
})
|
||||
@@ -527,7 +523,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
}
|
||||
validateCore(RecordsPost, req, body);
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
const records = await ops.create(body.records);
|
||||
res.json({records});
|
||||
})
|
||||
@@ -558,7 +554,7 @@ export class DocWorkerApi {
|
||||
this._app.post('/api/docs/:docId/tables/:tableId/columns', canEdit, validate(ColumnsPost),
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const body = req.body as Types.ColumnsPost;
|
||||
const {tableId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const actions = body.columns.map(({fields, id: colId}) =>
|
||||
// AddVisibleColumn adds the column to all widgets of the table.
|
||||
// This isn't necessarily what the user wants, but it seems like a good default.
|
||||
@@ -590,7 +586,7 @@ export class DocWorkerApi {
|
||||
|
||||
this._app.post('/api/docs/:docId/tables/:tableId/data/delete', canEdit, withDoc(async (activeDoc, req, res) => {
|
||||
const rowIds = req.body;
|
||||
const op = getTableOperations(req, activeDoc);
|
||||
const op = await getTableOperations(req, activeDoc);
|
||||
await op.destroy(rowIds);
|
||||
res.json(null);
|
||||
}));
|
||||
@@ -659,7 +655,7 @@ export class DocWorkerApi {
|
||||
const rowIds = columnValues.id;
|
||||
// sandbox expects no id column
|
||||
delete columnValues.id;
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
await ops.updateRecords(columnValues, rowIds);
|
||||
res.json(null);
|
||||
})
|
||||
@@ -669,7 +665,7 @@ export class DocWorkerApi {
|
||||
this._app.patch('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPatch),
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const body = req.body as Types.RecordsPatch;
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
await ops.update(body.records);
|
||||
res.json(null);
|
||||
})
|
||||
@@ -680,7 +676,7 @@ export class DocWorkerApi {
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
||||
const columnsTable = activeDoc.docData!.getMetaTable("_grist_Tables_column");
|
||||
const {tableId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const tableRef = tablesTable.findMatchingRowId({tableId});
|
||||
if (!tableRef) {
|
||||
throw new ApiError(`Table not found "${tableId}"`, 404);
|
||||
@@ -693,7 +689,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
return {...col, id};
|
||||
});
|
||||
const ops = getTableOperations(req, activeDoc, "_grist_Tables_column");
|
||||
const ops = await getTableOperations(req, activeDoc, "_grist_Tables_column");
|
||||
await ops.update(columns);
|
||||
res.json(null);
|
||||
})
|
||||
@@ -711,7 +707,7 @@ export class DocWorkerApi {
|
||||
}
|
||||
return {...table, id};
|
||||
});
|
||||
const ops = getTableOperations(req, activeDoc, "_grist_Tables");
|
||||
const ops = await getTableOperations(req, activeDoc, "_grist_Tables");
|
||||
await ops.update(tables);
|
||||
res.json(null);
|
||||
})
|
||||
@@ -720,7 +716,7 @@ export class DocWorkerApi {
|
||||
// Add or update records given in records format
|
||||
this._app.put('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPut),
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const ops = getTableOperations(req, activeDoc);
|
||||
const ops = await getTableOperations(req, activeDoc);
|
||||
const body = req.body as Types.RecordsPut;
|
||||
const options = {
|
||||
add: !isAffirmative(req.query.noadd),
|
||||
@@ -740,7 +736,7 @@ export class DocWorkerApi {
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
||||
const columnsTable = activeDoc.docData!.getMetaTable("_grist_Tables_column");
|
||||
const {tableId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const tableRef = tablesTable.findMatchingRowId({tableId});
|
||||
if (!tableRef) {
|
||||
throw new ApiError(`Table not found "${tableId}"`, 404);
|
||||
@@ -785,7 +781,8 @@ export class DocWorkerApi {
|
||||
|
||||
this._app.delete('/api/docs/:docId/tables/:tableId/columns/:colId', canEdit,
|
||||
withDoc(async (activeDoc, req, res) => {
|
||||
const {tableId, colId} = req.params;
|
||||
const {colId} = req.params;
|
||||
const tableId = await getRealTableId(req.params.tableId, {activeDoc, req});
|
||||
const actions = [ [ 'RemoveColumn', tableId, colId ] ];
|
||||
await handleSandboxError(tableId, [colId],
|
||||
activeDoc.applyUserActions(docSessionFromRequest(req), actions)
|
||||
@@ -1205,12 +1202,13 @@ export class DocWorkerApi {
|
||||
this._app.get('/api/docs/:docId/download/xlsx', canView, withDoc(async (activeDoc, req, res) => {
|
||||
// Query DB for doc metadata to get the doc title (to use as the filename).
|
||||
const {name: docTitle} = await this._dbManager.getDoc(req);
|
||||
const options = !_.isEmpty(req.query) ? this._getDownloadOptions(req, docTitle) : {
|
||||
const options: DownloadOptions = !_.isEmpty(req.query) ? this._getDownloadOptions(req, docTitle) : {
|
||||
filename: docTitle,
|
||||
tableId: '',
|
||||
viewSectionId: undefined,
|
||||
filters: [],
|
||||
sortOrder: [],
|
||||
header: 'label'
|
||||
};
|
||||
await downloadXLSX(activeDoc, req, res, options);
|
||||
}));
|
||||
@@ -1941,12 +1939,21 @@ function getErrorPlatform(tableId: string): TableOperationsPlatform {
|
||||
};
|
||||
}
|
||||
|
||||
function getTableOperations(req: RequestWithLogin, activeDoc: ActiveDoc, tableId?: string): TableOperationsImpl {
|
||||
export async function getMetaTables(activeDoc: ActiveDoc, req: RequestWithLogin) {
|
||||
return await handleSandboxError("", [],
|
||||
activeDoc.fetchMetaTables(docSessionFromRequest(req)));
|
||||
}
|
||||
|
||||
async function getTableOperations(
|
||||
req: RequestWithLogin,
|
||||
activeDoc: ActiveDoc,
|
||||
tableId?: string): Promise<TableOperationsImpl> {
|
||||
const options: OpOptions = {
|
||||
parseStrings: !isAffirmative(req.query.noparse)
|
||||
};
|
||||
const realTableId = await getRealTableId(tableId ?? req.params.tableId, {activeDoc, req});
|
||||
const platform: TableOperationsPlatform = {
|
||||
...getErrorPlatform(tableId ?? req.params.tableId),
|
||||
...getErrorPlatform(realTableId),
|
||||
applyUserActions(actions, opts) {
|
||||
if (!activeDoc) { throw new Error('no document'); }
|
||||
return activeDoc.applyUserActions(
|
||||
|
||||
@@ -17,7 +17,7 @@ import {BaseFormatter, createFullFormatterFromDocData} from 'app/common/ValueFor
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {RequestWithLogin} from 'app/server/lib/Authorizer';
|
||||
import {docSessionFromRequest} from 'app/server/lib/DocSession';
|
||||
import {optIntegerParam, optJsonParam, stringParam} from 'app/server/lib/requestUtils';
|
||||
import {optIntegerParam, optJsonParam, optStringParam, stringParam} from 'app/server/lib/requestUtils';
|
||||
import {ServerColumnGetters} from 'app/server/lib/ServerColumnGetters';
|
||||
import * as express from 'express';
|
||||
import * as _ from 'underscore';
|
||||
@@ -90,6 +90,8 @@ export interface ExportData {
|
||||
docSettings: DocumentSettings;
|
||||
}
|
||||
|
||||
export type ExportHeader = 'colId' | 'label';
|
||||
|
||||
/**
|
||||
* Export parameters that identifies a section, filters, sort order.
|
||||
*/
|
||||
@@ -99,6 +101,7 @@ export interface ExportParameters {
|
||||
sortOrder?: number[];
|
||||
filters?: Filter[];
|
||||
linkingFilter?: FilterColValues;
|
||||
header?: ExportHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +120,7 @@ export function parseExportParameters(req: express.Request): ExportParameters {
|
||||
const sortOrder = optJsonParam(req.query.activeSortSpec, []) as number[];
|
||||
const filters: Filter[] = optJsonParam(req.query.filters, []);
|
||||
const linkingFilter: FilterColValues = optJsonParam(req.query.linkingFilter, null);
|
||||
const header = optStringParam(req.query.header, 'header', {allowed: ['label', 'colId']}) as ExportHeader | undefined;
|
||||
|
||||
return {
|
||||
tableId,
|
||||
@@ -124,6 +128,7 @@ export function parseExportParameters(req: express.Request): ExportParameters {
|
||||
sortOrder,
|
||||
filters,
|
||||
linkingFilter,
|
||||
header,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {ApiError} from 'app/common/ApiError';
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {FilterColValues} from "app/common/ActiveDocAPI";
|
||||
import {DownloadOptions, ExportData, exportSection, exportTable, Filter} from 'app/server/lib/Export';
|
||||
import {DownloadOptions, ExportData, ExportHeader, exportSection, exportTable, Filter} from 'app/server/lib/Export';
|
||||
import log from 'app/server/lib/log';
|
||||
import * as bluebird from 'bluebird';
|
||||
import contentDisposition from 'content-disposition';
|
||||
@@ -17,11 +17,13 @@ bluebird.promisifyAll(csv);
|
||||
export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
|
||||
res: express.Response, options: DownloadOptions) {
|
||||
log.info('Generating .csv file...');
|
||||
const {filename, tableId, viewSectionId, filters, sortOrder, linkingFilter} = options;
|
||||
const {filename, tableId, viewSectionId, filters, sortOrder, linkingFilter, header} = options;
|
||||
const data = viewSectionId ?
|
||||
await makeCSVFromViewSection(
|
||||
activeDoc, viewSectionId, sortOrder || null, filters || null, linkingFilter || null, req) :
|
||||
await makeCSVFromTable(activeDoc, tableId, req);
|
||||
await makeCSVFromViewSection({
|
||||
activeDoc, viewSectionId, sortOrder: sortOrder || null, filters: filters || null,
|
||||
linkingFilter: linkingFilter || null, header, req
|
||||
}) :
|
||||
await makeCSVFromTable({activeDoc, tableId, header, req});
|
||||
res.set('Content-Type', 'text/csv');
|
||||
res.setHeader('Content-Disposition', contentDisposition(filename + '.csv'));
|
||||
res.send(data);
|
||||
@@ -32,36 +34,51 @@ export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
|
||||
*
|
||||
* See https://github.com/wdavidw/node-csv for API details.
|
||||
*
|
||||
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} filters (optional) - filters defined from ui.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} options.activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} options.filters (optional) - filters defined from ui.
|
||||
* @param {FilterColValues} options.linkingFilter (optional) - linking filter defined from ui.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
* @param {express.Request} options.req - the request object.
|
||||
*
|
||||
* @return {Promise<string>} Promise for the resulting CSV.
|
||||
*/
|
||||
export async function makeCSVFromViewSection(
|
||||
export async function makeCSVFromViewSection({
|
||||
activeDoc, viewSectionId, sortOrder = null, filters = null, linkingFilter = null, header, req
|
||||
}: {
|
||||
activeDoc: ActiveDoc,
|
||||
viewSectionId: number,
|
||||
sortOrder: number[] | null,
|
||||
filters: Filter[] | null,
|
||||
linkingFilter: FilterColValues | null,
|
||||
req: express.Request) {
|
||||
header?: ExportHeader,
|
||||
req: express.Request
|
||||
}) {
|
||||
|
||||
const data = await exportSection(activeDoc, viewSectionId, sortOrder, filters, linkingFilter, req);
|
||||
const file = convertToCsv(data);
|
||||
const file = convertToCsv(data, { header });
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a csv stream of a table that can be transformed or parsed.
|
||||
*
|
||||
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} tableId - id of the table to export.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.tableId - id of the table to export.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
* @param {express.Request} options.req - the request object.
|
||||
*
|
||||
* @return {Promise<string>} Promise for the resulting CSV.
|
||||
*/
|
||||
export async function makeCSVFromTable(
|
||||
export async function makeCSVFromTable({ activeDoc, tableId, header, req }: {
|
||||
activeDoc: ActiveDoc,
|
||||
tableId: string,
|
||||
req: express.Request) {
|
||||
header?: ExportHeader,
|
||||
req: express.Request
|
||||
}) {
|
||||
|
||||
if (!activeDoc.docData) {
|
||||
throw new Error('No docData in active document');
|
||||
@@ -76,7 +93,7 @@ export async function makeCSVFromTable(
|
||||
}
|
||||
|
||||
const data = await exportTable(activeDoc, tableRef, req);
|
||||
const file = convertToCsv(data);
|
||||
const file = convertToCsv(data, { header });
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -84,13 +101,13 @@ function convertToCsv({
|
||||
rowIds,
|
||||
access,
|
||||
columns: viewColumns,
|
||||
docSettings
|
||||
}: ExportData) {
|
||||
}: ExportData, options: { header?: ExportHeader }) {
|
||||
|
||||
// create formatters for columns
|
||||
const formatters = viewColumns.map(col => col.formatter);
|
||||
// Arrange the data into a row-indexed matrix, starting with column headers.
|
||||
const csvMatrix = [viewColumns.map(col => col.label)];
|
||||
const colPropertyAsHeader = options.header ?? 'label';
|
||||
const csvMatrix = [viewColumns.map(col => col[colPropertyAsHeader])];
|
||||
// populate all the rows with values as strings
|
||||
rowIds.forEach(row => {
|
||||
csvMatrix.push(access.map((getter, c) => formatters[c].formatAny(getter(row))));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {PassThrough} from 'stream';
|
||||
import {FilterColValues} from "app/common/ActiveDocAPI";
|
||||
import {ActiveDocSource, doExportDoc, doExportSection, doExportTable,
|
||||
ExportData, ExportParameters, Filter} from 'app/server/lib/Export';
|
||||
ExportData, ExportHeader, ExportParameters, Filter} from 'app/server/lib/Export';
|
||||
import {createExcelFormatter} from 'app/server/lib/ExcelFormatter';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {Alignment, Border, Buffer as ExcelBuffer, stream as ExcelWriteStream,
|
||||
@@ -79,26 +79,34 @@ export async function doMakeXLSXFromOptions(
|
||||
stream: Stream,
|
||||
options: ExportParameters
|
||||
) {
|
||||
const {tableId, viewSectionId, filters, sortOrder, linkingFilter} = options;
|
||||
const {tableId, viewSectionId, filters, sortOrder, linkingFilter, header} = options;
|
||||
if (viewSectionId) {
|
||||
return doMakeXLSXFromViewSection(activeDocSource, testDates, stream, viewSectionId,
|
||||
sortOrder || null, filters || null, linkingFilter || null);
|
||||
return doMakeXLSXFromViewSection({activeDocSource, testDates, stream, viewSectionId, header,
|
||||
sortOrder: sortOrder || null, filters: filters || null, linkingFilter: linkingFilter || null});
|
||||
} else if (tableId) {
|
||||
return doMakeXLSXFromTable(activeDocSource, testDates, stream, tableId);
|
||||
return doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, header});
|
||||
} else {
|
||||
return doMakeXLSX(activeDocSource, testDates, stream);
|
||||
return doMakeXLSX({activeDocSource, testDates, stream, header});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Returns a XLSX stream of a view section that can be transformed or parsed.
|
||||
*
|
||||
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} filters (optional) - filters defined from ui.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDocSource - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.viewSectionId - id of the viewsection to export.
|
||||
* @param {Integer[]} options.activeSortOrder (optional) - overriding sort order.
|
||||
* @param {Filter[]} options.filters (optional) - filters defined from ui.
|
||||
* @param {FilterColValues} options.linkingFilter (optional)
|
||||
* @param {Stream} options.stream - the stream to write to.
|
||||
* @param {boolean} options.testDates - whether to use static dates for testing.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
*/
|
||||
async function doMakeXLSXFromViewSection(
|
||||
async function doMakeXLSXFromViewSection({
|
||||
activeDocSource, testDates, stream, viewSectionId, sortOrder, filters, linkingFilter, header
|
||||
}: {
|
||||
activeDocSource: ActiveDocSource,
|
||||
testDates: boolean,
|
||||
stream: Stream,
|
||||
@@ -106,27 +114,35 @@ async function doMakeXLSXFromViewSection(
|
||||
sortOrder: number[] | null,
|
||||
filters: Filter[] | null,
|
||||
linkingFilter: FilterColValues | null,
|
||||
) {
|
||||
header?: ExportHeader,
|
||||
}) {
|
||||
const data = await doExportSection(activeDocSource, viewSectionId, sortOrder, filters, linkingFilter);
|
||||
const {exportTable, end} = convertToExcel(stream, testDates);
|
||||
const {exportTable, end} = convertToExcel(stream, testDates, {header});
|
||||
exportTable(data);
|
||||
return end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Returns a XLSX stream of a table that can be transformed or parsed.
|
||||
*
|
||||
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} tableId - id of the table to export.
|
||||
* @param {Object} options - options for the export.
|
||||
* @param {Object} options.activeDocSource - the activeDoc that the table being converted belongs to.
|
||||
* @param {Integer} options.tableId - id of the table to export.
|
||||
* @param {Stream} options.stream - the stream to write to.
|
||||
* @param {boolean} options.testDates - whether to use static dates for testing.
|
||||
* @param {string} options.header (optional) - which field of the column to use as header
|
||||
*
|
||||
*/
|
||||
async function doMakeXLSXFromTable(
|
||||
async function doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, header}: {
|
||||
activeDocSource: ActiveDocSource,
|
||||
testDates: boolean,
|
||||
stream: Stream,
|
||||
tableId: string,
|
||||
) {
|
||||
header?: ExportHeader,
|
||||
}) {
|
||||
const data = await doExportTable(activeDocSource, {tableId});
|
||||
const {exportTable, end} = convertToExcel(stream, testDates);
|
||||
const {exportTable, end} = convertToExcel(stream, testDates, {header});
|
||||
exportTable(data);
|
||||
return end();
|
||||
}
|
||||
@@ -134,12 +150,13 @@ async function doMakeXLSXFromTable(
|
||||
/**
|
||||
* Creates excel document with all tables from an active Grist document.
|
||||
*/
|
||||
async function doMakeXLSX(
|
||||
async function doMakeXLSX({activeDocSource, testDates, stream, header}: {
|
||||
activeDocSource: ActiveDocSource,
|
||||
testDates: boolean,
|
||||
stream: Stream,
|
||||
): Promise<void|ExcelBuffer> {
|
||||
const {exportTable, end} = convertToExcel(stream, testDates);
|
||||
header?: ExportHeader,
|
||||
}): Promise<void|ExcelBuffer> {
|
||||
const {exportTable, end} = convertToExcel(stream, testDates, {header});
|
||||
await doExportDoc(activeDocSource, async (table: ExportData) => exportTable(table));
|
||||
return end();
|
||||
}
|
||||
@@ -152,7 +169,7 @@ async function doMakeXLSX(
|
||||
* (The second option is for grist-static; at the time of writing
|
||||
* WorkbookWriter doesn't appear to be available in a browser context).
|
||||
*/
|
||||
function convertToExcel(stream: Stream|undefined, testDates: boolean): {
|
||||
function convertToExcel(stream: Stream|undefined, testDates: boolean, options: { header?: ExportHeader }): {
|
||||
exportTable: (table: ExportData) => void,
|
||||
end: () => Promise<void|ExcelBuffer>,
|
||||
} {
|
||||
@@ -206,7 +223,8 @@ function convertToExcel(stream: Stream|undefined, testDates: boolean): {
|
||||
const formatters = columns.map(col => createExcelFormatter(col.formatter.type, col.formatter.widgetOpts));
|
||||
// Generate headers for all columns with correct styles for whole column.
|
||||
// Actual header style for a first row will be overwritten later.
|
||||
ws.columns = columns.map((col, c) => ({ header: col.label, style: formatters[c].style() }));
|
||||
const colHeader = options.header ?? 'label';
|
||||
ws.columns = columns.map((col, c) => ({ header: col[colHeader], style: formatters[c].style() }));
|
||||
// style up the header row
|
||||
for (let i = 1; i <= columns.length; i++) {
|
||||
// apply to all rows (including header)
|
||||
|
||||
Reference in New Issue
Block a user