Skip to content

Commit

Permalink
Change colIdAsHeader to header=colId|label #688
Browse files Browse the repository at this point in the history
  • Loading branch information
fflorent authored and Florent F committed Oct 13, 2023
1 parent 2fce0e2 commit 4410bc0
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 40 deletions.
4 changes: 2 additions & 2 deletions app/server/lib/DocApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1205,13 +1205,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: [],
colIdAsHeader: false
header: 'label'
};
await downloadXLSX(activeDoc, req, res, options);
}));
Expand Down
10 changes: 6 additions & 4 deletions app/server/lib/Export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -90,6 +90,8 @@ export interface ExportData {
docSettings: DocumentSettings;
}

export type ExportHeader = 'colId' | 'label';

/**
* Export parameters that identifies a section, filters, sort order.
*/
Expand All @@ -99,7 +101,7 @@ export interface ExportParameters {
sortOrder?: number[];
filters?: Filter[];
linkingFilter?: FilterColValues;
colIdAsHeader?: boolean;
header?: ExportHeader;
}

/**
Expand All @@ -118,15 +120,15 @@ 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 colIdAsHeader = gutil.isAffirmative(req.query.colIdAsHeader);
const header = optStringParam(req.query.header, 'header', {allowed: ['label', 'colId']}) as ExportHeader | undefined;

return {
tableId,
viewSectionId,
sortOrder,
filters,
linkingFilter,
colIdAsHeader,
header,
};
}

Expand Down
28 changes: 14 additions & 14 deletions app/server/lib/ExportCSV.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -17,13 +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, colIdAsHeader} = options;
const {filename, tableId, viewSectionId, filters, sortOrder, linkingFilter, header} = options;
const data = viewSectionId ?
await makeCSVFromViewSection({
activeDoc, viewSectionId, sortOrder: sortOrder || null, filters: filters || null,
linkingFilter: linkingFilter || null, colIdAsHeader, req
linkingFilter: linkingFilter || null, header, req
}) :
await makeCSVFromTable({activeDoc, tableId, colIdAsHeader, req});
await makeCSVFromTable({activeDoc, tableId, header, req});
res.set('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', contentDisposition(filename + '.csv'));
res.send(data);
Expand All @@ -40,25 +40,25 @@ export async function downloadCSV(activeDoc: ActiveDoc, req: express.Request,
* @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 {boolean} [options.colIdAsHeader] - whether to use column id as header.
* @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({
activeDoc, viewSectionId, sortOrder = null, filters = null, linkingFilter = null, colIdAsHeader, req
activeDoc, viewSectionId, sortOrder = null, filters = null, linkingFilter = null, header, req
}: {
activeDoc: ActiveDoc,
viewSectionId: number,
sortOrder: number[] | null,
filters: Filter[] | null,
linkingFilter: FilterColValues | null,
colIdAsHeader?: boolean,
header?: ExportHeader,
req: express.Request
}) {

const data = await exportSection(activeDoc, viewSectionId, sortOrder, filters, linkingFilter, req);
const file = convertToCsv(data, { colIdAsHeader });
const file = convertToCsv(data, { header });
return file;
}

Expand All @@ -68,15 +68,15 @@ export async function makeCSVFromViewSection({
* @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 {boolean} [options.colIdAsHeader] - whether to use column id as header.
* @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({ activeDoc, tableId, colIdAsHeader, req }: {
export async function makeCSVFromTable({ activeDoc, tableId, header, req }: {
activeDoc: ActiveDoc,
tableId: string,
colIdAsHeader?: boolean,
header?: ExportHeader,
req: express.Request
}) {

Expand All @@ -93,20 +93,20 @@ export async function makeCSVFromTable({ activeDoc, tableId, colIdAsHeader, req
}

const data = await exportTable(activeDoc, tableRef, req);
const file = convertToCsv(data, { colIdAsHeader });
const file = convertToCsv(data, { header });
return file;
}

function convertToCsv({
rowIds,
access,
columns: viewColumns,
}: ExportData, options: { colIdAsHeader?: boolean }) {
}: 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 colPropertyAsHeader = options.colIdAsHeader ? 'colId' : 'label';
const colPropertyAsHeader = options.header ?? 'label';
const csvMatrix = [viewColumns.map(col => col[colPropertyAsHeader])];
// populate all the rows with values as strings
rowIds.forEach(row => {
Expand Down
36 changes: 18 additions & 18 deletions app/server/lib/workerExporter.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -79,14 +79,14 @@ export async function doMakeXLSXFromOptions(
stream: Stream,
options: ExportParameters
) {
const {tableId, viewSectionId, filters, sortOrder, linkingFilter, colIdAsHeader} = options;
const {tableId, viewSectionId, filters, sortOrder, linkingFilter, header} = options;
if (viewSectionId) {
return doMakeXLSXFromViewSection({activeDocSource, testDates, stream, viewSectionId, colIdAsHeader,
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, colIdAsHeader});
return doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, header});
} else {
return doMakeXLSX({activeDocSource, testDates, stream, colIdAsHeader});
return doMakeXLSX({activeDocSource, testDates, stream, header});
}
}

Expand All @@ -102,10 +102,10 @@ export async function doMakeXLSXFromOptions(
* @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 {boolean} [options.colIdAsHeader] - whether to use column id as header.
* @param {string} options.header (optional) - which field of the column to use as header
*/
async function doMakeXLSXFromViewSection({
activeDocSource, testDates, stream, viewSectionId, sortOrder, filters, linkingFilter, colIdAsHeader
activeDocSource, testDates, stream, viewSectionId, sortOrder, filters, linkingFilter, header
}: {
activeDocSource: ActiveDocSource,
testDates: boolean,
Expand All @@ -114,10 +114,10 @@ async function doMakeXLSXFromViewSection({
sortOrder: number[] | null,
filters: Filter[] | null,
linkingFilter: FilterColValues | null,
colIdAsHeader?: boolean,
header?: ExportHeader,
}) {
const data = await doExportSection(activeDocSource, viewSectionId, sortOrder, filters, linkingFilter);
const {exportTable, end} = convertToExcel(stream, testDates, {colIdAsHeader});
const {exportTable, end} = convertToExcel(stream, testDates, {header});
exportTable(data);
return end();
}
Expand All @@ -131,32 +131,32 @@ async function doMakeXLSXFromViewSection({
* @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 {boolean} [options.colIdAsHeader] - whether to use column id as header.
* @param {string} options.header (optional) - which field of the column to use as header
*
*/
async function doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, colIdAsHeader}: {
async function doMakeXLSXFromTable({activeDocSource, testDates, stream, tableId, header}: {
activeDocSource: ActiveDocSource,
testDates: boolean,
stream: Stream,
tableId: string,
colIdAsHeader?: boolean,
header?: ExportHeader,
}) {
const data = await doExportTable(activeDocSource, {tableId});
const {exportTable, end} = convertToExcel(stream, testDates, {colIdAsHeader});
const {exportTable, end} = convertToExcel(stream, testDates, {header});
exportTable(data);
return end();
}

/**
* Creates excel document with all tables from an active Grist document.
*/
async function doMakeXLSX({activeDocSource, testDates, stream, colIdAsHeader}: {
async function doMakeXLSX({activeDocSource, testDates, stream, header}: {
activeDocSource: ActiveDocSource,
testDates: boolean,
stream: Stream,
colIdAsHeader?: boolean,
header?: ExportHeader,
}): Promise<void|ExcelBuffer> {
const {exportTable, end} = convertToExcel(stream, testDates, {colIdAsHeader});
const {exportTable, end} = convertToExcel(stream, testDates, {header});
await doExportDoc(activeDocSource, async (table: ExportData) => exportTable(table));
return end();
}
Expand All @@ -169,7 +169,7 @@ async function doMakeXLSX({activeDocSource, testDates, stream, colIdAsHeader}: {
* (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, options: { colIdAsHeader?: boolean }): {
function convertToExcel(stream: Stream|undefined, testDates: boolean, options: { header?: ExportHeader }): {
exportTable: (table: ExportData) => void,
end: () => Promise<void|ExcelBuffer>,
} {
Expand Down Expand Up @@ -223,7 +223,7 @@ function convertToExcel(stream: Stream|undefined, testDates: boolean, options: {
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.
const colHeader = options.colIdAsHeader ? 'colId' : 'label';
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++) {
Expand Down
4 changes: 2 additions & 2 deletions test/server/lib/DocApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2592,7 +2592,7 @@ function testDocApi() {
assert.equal(resp2.data, 'A,B\nSanta,1\nBob,11\nAlice,2\nFelix,22\n');
});

it('GET /docs/{did}/download/csv with colIdAsHeader shows columns id in the header instead of their name',
it('GET /docs/{did}/download/csv with header=colId shows columns id in the header instead of their name',
async function () {
const { docUrl } = await generateDocAndUrl('csvWithColIdAsHeader');
const AColRef = 2;
Expand All @@ -2606,7 +2606,7 @@ function testDocApi() {
];
const resp = await axios.post(`${docUrl}/apply`, userActions, chimpy);
assert.equal(resp.status, 200);
const csvResp = await axios.get(`${docUrl}/download/csv?tableId=Table1&colIdAsHeader=true`, chimpy);
const csvResp = await axios.get(`${docUrl}/download/csv?tableId=Table1&header=colId`, chimpy);
assert.equal(csvResp.status, 200);
assert.equal(csvResp.data, 'AColId,B,C\na1,b1,\n');
});
Expand Down

0 comments on commit 4410bc0

Please sign in to comment.