From 2dc07f1659daa0db448b05c34efcf3bbaa7f6cd4 Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Mon, 9 Oct 2023 10:42:03 +0200 Subject: [PATCH 1/7] feat add technical id for tableId --- app/server/lib/ActiveDoc.ts | 18 ++++++++++++++++++ app/server/lib/DocApi.ts | 5 +++-- buildtools/prepare_python.sh | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index 0413f2eae2..93ead73435 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -2784,6 +2784,10 @@ export function tableIdToRef(metaTables: { [p: string]: TableDataAction }, table const [, , tableRefs, tableData] = metaTables._grist_Tables; const tableRowIndex = tableData.tableId.indexOf(tableId); if (tableRowIndex === -1) { + // Test if the tableId argument is directly the ref + if(parseInt(tableId) && tableRefs.indexOf(parseInt(tableId)) >= 0){ + return parseInt(tableId); + } throw new ApiError(`Table not found "${tableId}"`, 404); } return tableRefs[tableRowIndex]; @@ -2799,11 +2803,25 @@ export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId: columnData.colId[i] === colId && columnData.parentId[i] === tableRef )); if (colRowIndex === -1) { + // Test if the colId argument is directly the ref + if(parseInt(colId) && colRefs.indexOf(parseInt(colId)) >= 0){ + return parseInt(colId); + } throw new ApiError(`Column not found "${colId}"`, 404); } return colRefs[colRowIndex]; } +// Helper that check if tableRef is used instead of tableId and return real tableId +export function getRealTableId(metaTables: { [p: string]: TableDataAction }, tableId: string): string { + const [, , tableRefs, tableData] = metaTables._grist_Tables; + if(parseInt(tableId) && 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']); } diff --git a/app/server/lib/DocApi.ts b/app/server/lib/DocApi.ts index bcf93c7a36..aff6e39f13 100644 --- a/app/server/lib/DocApi.ts +++ b/app/server/lib/DocApi.ts @@ -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,8 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, optTableId || req.params.tableId); const session = docSessionFromRequest(req); const {tableData} = await handleSandboxError(tableId, [], activeDoc.fetchQuery( session, {tableId, filters}, !immediate)); diff --git a/buildtools/prepare_python.sh b/buildtools/prepare_python.sh index 716f979c23..34c4ec4d3b 100755 --- a/buildtools/prepare_python.sh +++ b/buildtools/prepare_python.sh @@ -28,3 +28,4 @@ else # Make sure python3 isn't around. rm -rf sandbox_venv3 fi +echo "toto" From 60fc3800ce0ed8173d4ad2e4ff143040262386b9 Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Mon, 9 Oct 2023 11:33:02 +0200 Subject: [PATCH 2/7] delete echo --- buildtools/prepare_python.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/buildtools/prepare_python.sh b/buildtools/prepare_python.sh index 34c4ec4d3b..716f979c23 100755 --- a/buildtools/prepare_python.sh +++ b/buildtools/prepare_python.sh @@ -28,4 +28,3 @@ else # Make sure python3 isn't around. rm -rf sandbox_venv3 fi -echo "toto" From 89f4066f9a67c700df40b94370562cf0d89bc00c Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Mon, 9 Oct 2023 15:30:08 +0200 Subject: [PATCH 3/7] add technical tableId compatibility in api calls for post and patch --- app/server/lib/ActiveDoc.ts | 4 ---- app/server/lib/DocApi.ts | 43 ++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index 93ead73435..cc1d7003e0 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -2784,10 +2784,6 @@ export function tableIdToRef(metaTables: { [p: string]: TableDataAction }, table const [, , tableRefs, tableData] = metaTables._grist_Tables; const tableRowIndex = tableData.tableId.indexOf(tableId); if (tableRowIndex === -1) { - // Test if the tableId argument is directly the ref - if(parseInt(tableId) && tableRefs.indexOf(parseInt(tableId)) >= 0){ - return parseInt(tableId); - } throw new ApiError(`Table not found "${tableId}"`, 404); } return tableRefs[tableRowIndex]; diff --git a/app/server/lib/DocApi.ts b/app/server/lib/DocApi.ts index aff6e39f13..7fd397e1e7 100644 --- a/app/server/lib/DocApi.ts +++ b/app/server/lib/DocApi.ts @@ -338,7 +338,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 = getRealTableId(metaTables, req.params.tableId || webhook.tableId); + const fields: Partial = {}; if (url && !isUrlAllowed(url)) { @@ -388,7 +389,8 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); const includeHidden = isAffirmative(req.query.hidden); const columns = await handleSandboxError('', [], activeDoc.getTableCols(docSessionFromRequest(req), tableId, includeHidden)); @@ -499,7 +501,9 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); + const op = getTableOperations(req, activeDoc, tableId); const ids = await op.addRecords(count, colValues); res.json(ids); }) @@ -528,7 +532,9 @@ export class DocWorkerApi { } } validateCore(RecordsPost, req, body); - const ops = getTableOperations(req, activeDoc); + const metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); + const ops = getTableOperations(req, activeDoc, tableId); const records = await ops.create(body.records); res.json({records}); }) @@ -559,7 +565,8 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); 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. @@ -591,7 +598,9 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); + const op = getTableOperations(req, activeDoc, tableId); await op.destroy(rowIds); res.json(null); })); @@ -660,7 +669,9 @@ export class DocWorkerApi { const rowIds = columnValues.id; // sandbox expects no id column delete columnValues.id; - const ops = getTableOperations(req, activeDoc); + const metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); + const ops = getTableOperations(req, activeDoc, tableId); await ops.updateRecords(columnValues, rowIds); res.json(null); }) @@ -670,7 +681,9 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); + const ops = getTableOperations(req, activeDoc, tableId); await ops.update(body.records); res.json(null); }) @@ -681,7 +694,8 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); const tableRef = tablesTable.findMatchingRowId({tableId}); if (!tableRef) { throw new ApiError(`Table not found "${tableId}"`, 404); @@ -721,7 +735,9 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); + const ops = getTableOperations(req, activeDoc, tableId); const body = req.body as Types.RecordsPut; const options = { add: !isAffirmative(req.query.noadd), @@ -741,7 +757,8 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); const tableRef = tablesTable.findMatchingRowId({tableId}); if (!tableRef) { throw new ApiError(`Table not found "${tableId}"`, 404); @@ -786,7 +803,9 @@ 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 metaTables = await getMetaTables(activeDoc, req); + const tableId = getRealTableId(metaTables, req.params.tableId); const actions = [ [ 'RemoveColumn', tableId, colId ] ]; await handleSandboxError(tableId, [colId], activeDoc.applyUserActions(docSessionFromRequest(req), actions) From 29461071cfd17d8b086549697948ad0b19d58405 Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Mon, 9 Oct 2023 16:15:19 +0200 Subject: [PATCH 4/7] remove unappropiate code --- app/server/lib/ActiveDoc.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index cc1d7003e0..dd0ffd0b7e 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -2799,10 +2799,6 @@ export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId: columnData.colId[i] === colId && columnData.parentId[i] === tableRef )); if (colRowIndex === -1) { - // Test if the colId argument is directly the ref - if(parseInt(colId) && colRefs.indexOf(parseInt(colId)) >= 0){ - return parseInt(colId); - } throw new ApiError(`Column not found "${colId}"`, 404); } return colRefs[colRowIndex]; From 647bb5774584e417b7637156a85fbaa965393b4b Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Tue, 10 Oct 2023 11:13:20 +0200 Subject: [PATCH 5/7] remove line to reload test in github --- app/server/lib/ActiveDoc.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index dd0ffd0b7e..2f60e8fd0a 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -2791,7 +2791,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; From 64237f9d9d69ecc01e725bbf3878d9d989ab75f9 Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Thu, 12 Oct 2023 12:28:51 +0200 Subject: [PATCH 6/7] refactor the call to getRealTableId --- app/server/lib/ActiveDoc.ts | 28 ++++++++++++---- app/server/lib/DocApi.ts | 66 +++++++++++++++---------------------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index 2f60e8fd0a..96bc80ea2b 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -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); @@ -2804,11 +2805,26 @@ export function colIdToRef(metaTables: {[p: string]: TableDataAction}, tableId: } // Helper that check if tableRef is used instead of tableId and return real tableId -export function getRealTableId(metaTables: { [p: string]: TableDataAction }, tableId: string): string { - const [, , tableRefs, tableData] = metaTables._grist_Tables; - if(parseInt(tableId) && tableRefs.indexOf(parseInt(tableId)) >= 0){ - const tableRowIndex = tableRefs.indexOf(parseInt(tableId)); - return tableData.tableId[tableRowIndex]!.toString(); +// 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 { + 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; } diff --git a/app/server/lib/DocApi.ts b/app/server/lib/DocApi.ts index 7fd397e1e7..095cd307e2 100644 --- a/app/server/lib/DocApi.ts +++ b/app/server/lib/DocApi.ts @@ -201,8 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, 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)); @@ -263,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) { @@ -338,7 +332,7 @@ 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 = getRealTableId(metaTables, req.params.tableId || webhook.tableId); + const tableId = await getRealTableId(req.params.tableId || webhook.tableId, {metaTables}); const fields: Partial = {}; @@ -389,8 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, 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)); @@ -501,9 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); - const op = getTableOperations(req, activeDoc, tableId); + const op = await getTableOperations(req, activeDoc); const ids = await op.addRecords(count, colValues); res.json(ids); }) @@ -532,9 +523,7 @@ export class DocWorkerApi { } } validateCore(RecordsPost, req, body); - const metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); - const ops = getTableOperations(req, activeDoc, tableId); + const ops = await getTableOperations(req, activeDoc); const records = await ops.create(body.records); res.json({records}); }) @@ -565,8 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); + 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. @@ -598,9 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); - const op = getTableOperations(req, activeDoc, tableId); + const op = await getTableOperations(req, activeDoc); await op.destroy(rowIds); res.json(null); })); @@ -669,9 +655,7 @@ export class DocWorkerApi { const rowIds = columnValues.id; // sandbox expects no id column delete columnValues.id; - const metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); - const ops = getTableOperations(req, activeDoc, tableId); + const ops = await getTableOperations(req, activeDoc); await ops.updateRecords(columnValues, rowIds); res.json(null); }) @@ -681,9 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); - const ops = getTableOperations(req, activeDoc, tableId); + const ops = await getTableOperations(req, activeDoc); await ops.update(body.records); res.json(null); }) @@ -694,8 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); + const tableId = await getRealTableId(req.params.tableId, {activeDoc, req}); const tableRef = tablesTable.findMatchingRowId({tableId}); if (!tableRef) { throw new ApiError(`Table not found "${tableId}"`, 404); @@ -708,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); }) @@ -726,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); }) @@ -735,9 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); - const ops = getTableOperations(req, activeDoc, tableId); + const ops = await getTableOperations(req, activeDoc); const body = req.body as Types.RecordsPut; const options = { add: !isAffirmative(req.query.noadd), @@ -757,8 +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 metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); + const tableId = await getRealTableId(req.params.tableId, {activeDoc, req}); const tableRef = tablesTable.findMatchingRowId({tableId}); if (!tableRef) { throw new ApiError(`Table not found "${tableId}"`, 404); @@ -804,8 +782,7 @@ export class DocWorkerApi { this._app.delete('/api/docs/:docId/tables/:tableId/columns/:colId', canEdit, withDoc(async (activeDoc, req, res) => { const {colId} = req.params; - const metaTables = await getMetaTables(activeDoc, req); - const tableId = getRealTableId(metaTables, req.params.tableId); + const tableId = await getRealTableId(req.params.tableId, {activeDoc, req}); const actions = [ [ 'RemoveColumn', tableId, colId ] ]; await handleSandboxError(tableId, [colId], activeDoc.applyUserActions(docSessionFromRequest(req), actions) @@ -1961,12 +1938,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 { 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( From b7be7571457eb0b3c1b5921291f9788ec2cf683e Mon Sep 17 00:00:00 2001 From: CamilleLegeron Date: Thu, 12 Oct 2023 18:35:43 +0200 Subject: [PATCH 7/7] add test for get call api --- test/server/lib/DocApi.ts | 359 ++++++++++++++++++++------------------ 1 file changed, 192 insertions(+), 167 deletions(-) diff --git a/test/server/lib/DocApi.ts b/test/server/lib/DocApi.ts index 8c7750d286..dabb2fe0bd 100644 --- a/test/server/lib/DocApi.ts +++ b/test/server/lib/DocApi.ts @@ -547,9 +547,7 @@ function testDocApi() { } it("GET /docs/{did}/tables/{tid}/data retrieves data in column format", async function () { - const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/data`, chimpy); - assert.equal(resp.status, 200); - assert.deepEqual(resp.data, { + const data = { id: [1, 2, 3, 4], A: ['hello', '', '', ''], B: ['', 'world', '', ''], @@ -557,68 +555,73 @@ function testDocApi() { D: [null, null, null, null], E: ['HELLO', '', '', ''], manualSort: [1, 2, 3, 4] - }); + }; + const respWithTableId = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/data`, chimpy); + assert.equal(respWithTableId.status, 200); + assert.deepEqual(respWithTableId.data, data); + const respWithTableRef = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/data`, chimpy); + assert.equal(respWithTableRef.status, 200); + assert.deepEqual(respWithTableRef.data, data); }); it("GET /docs/{did}/tables/{tid}/records retrieves data in records format", async function () { - const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`, chimpy); - assert.equal(resp.status, 200); - assert.deepEqual(resp.data, - { - records: - [ - { - id: 1, - fields: { - A: 'hello', - B: '', - C: '', - D: null, - E: 'HELLO', - }, + const data = { + records: + [ + { + id: 1, + fields: { + A: 'hello', + B: '', + C: '', + D: null, + E: 'HELLO', }, - { - id: 2, - fields: { - A: '', - B: 'world', - C: '', - D: null, - E: '', - }, + }, + { + id: 2, + fields: { + A: '', + B: 'world', + C: '', + D: null, + E: '', }, - { - id: 3, - fields: { - A: '', - B: '', - C: '', - D: null, - E: '', - }, + }, + { + id: 3, + fields: { + A: '', + B: '', + C: '', + D: null, + E: '', }, - { - id: 4, - fields: { - A: '', - B: '', - C: '', - D: null, - E: '', - }, + }, + { + id: 4, + fields: { + A: '', + B: '', + C: '', + D: null, + E: '', }, - ] - }); + }, + ] + }; + const respWithTableId = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`, chimpy); + assert.equal(respWithTableId.status, 200); + assert.deepEqual(respWithTableId.data, data); + const respWithTableRef = await axios.get( + `${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/records`, chimpy); + assert.equal(respWithTableRef.status, 200); + assert.deepEqual(respWithTableRef.data, data); }); it('GET /docs/{did}/tables/{tid}/records honors the "hidden" param', async function () { const params = { hidden: true }; - const resp = await axios.get( - `${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`, - {...chimpy, params } - ); - assert.equal(resp.status, 200); - assert.deepEqual(resp.data.records[0], { + const data = { id: 1, fields: { manualSort: 1, @@ -628,7 +631,19 @@ function testDocApi() { D: null, E: 'HELLO', }, - }); + }; + const respWithTableId = await axios.get( + `${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/records`, + {...chimpy, params } + ); + assert.equal(respWithTableId.status, 200); + assert.deepEqual(respWithTableId.data.records[0], data); + const respWithTableRef = await axios.get( + `${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/records`, + {...chimpy, params } + ); + assert.equal(respWithTableRef.status, 200); + assert.deepEqual(respWithTableRef.data.records[0], data); }); it("GET /docs/{did}/tables/{tid}/records handles errors and hidden columns", async function () { @@ -683,119 +698,121 @@ function testDocApi() { }); it("GET /docs/{did}/tables/{tid}/columns retrieves columns", async function () { - const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/columns`, chimpy); - assert.equal(resp.status, 200); - assert.deepEqual(resp.data, - { - columns: [ - { - id: 'A', - fields: { - colRef: 2, - parentId: 1, - parentPos: 1, - type: 'Text', - widgetOptions: '', - isFormula: false, - formula: '', - label: 'A', - description: '', - untieColIdFromLabel: false, - summarySourceCol: 0, - displayCol: 0, - visibleCol: 0, - rules: null, - recalcWhen: 0, - recalcDeps: null - } - }, - { - id: 'B', - fields: { - colRef: 3, - parentId: 1, - parentPos: 2, - type: 'Text', - widgetOptions: '', - isFormula: false, - formula: '', - label: 'B', - description: '', - untieColIdFromLabel: false, - summarySourceCol: 0, - displayCol: 0, - visibleCol: 0, - rules: null, - recalcWhen: 0, - recalcDeps: null - } - }, - { - id: 'C', - fields: { - colRef: 4, - parentId: 1, - parentPos: 3, - type: 'Text', - widgetOptions: '', - isFormula: false, - formula: '', - label: 'C', - description: '', - untieColIdFromLabel: false, - summarySourceCol: 0, - displayCol: 0, - visibleCol: 0, - rules: null, - recalcWhen: 0, - recalcDeps: null - } - }, - { - id: 'D', - fields: { - colRef: 5, - parentId: 1, - parentPos: 3, - type: 'Any', - widgetOptions: '', - isFormula: true, - formula: '', - label: 'D', - description: '', - untieColIdFromLabel: false, - summarySourceCol: 0, - displayCol: 0, - visibleCol: 0, - rules: null, - recalcWhen: 0, - recalcDeps: null - } - }, - { - id: 'E', - fields: { - colRef: 6, - parentId: 1, - parentPos: 4, - type: 'Any', - widgetOptions: '', - isFormula: true, - formula: '$A.upper()', - label: 'E', - description: '', - untieColIdFromLabel: false, - summarySourceCol: 0, - displayCol: 0, - visibleCol: 0, - rules: null, - recalcWhen: 0, - recalcDeps: null - } + const data = { + columns: [ + { + id: 'A', + fields: { + colRef: 2, + parentId: 1, + parentPos: 1, + type: 'Text', + widgetOptions: '', + isFormula: false, + formula: '', + label: 'A', + description: '', + untieColIdFromLabel: false, + summarySourceCol: 0, + displayCol: 0, + visibleCol: 0, + rules: null, + recalcWhen: 0, + recalcDeps: null } - ] - } - ); + }, + { + id: 'B', + fields: { + colRef: 3, + parentId: 1, + parentPos: 2, + type: 'Text', + widgetOptions: '', + isFormula: false, + formula: '', + label: 'B', + description: '', + untieColIdFromLabel: false, + summarySourceCol: 0, + displayCol: 0, + visibleCol: 0, + rules: null, + recalcWhen: 0, + recalcDeps: null + } + }, + { + id: 'C', + fields: { + colRef: 4, + parentId: 1, + parentPos: 3, + type: 'Text', + widgetOptions: '', + isFormula: false, + formula: '', + label: 'C', + description: '', + untieColIdFromLabel: false, + summarySourceCol: 0, + displayCol: 0, + visibleCol: 0, + rules: null, + recalcWhen: 0, + recalcDeps: null + } + }, + { + id: 'D', + fields: { + colRef: 5, + parentId: 1, + parentPos: 3, + type: 'Any', + widgetOptions: '', + isFormula: true, + formula: '', + label: 'D', + description: '', + untieColIdFromLabel: false, + summarySourceCol: 0, + displayCol: 0, + visibleCol: 0, + rules: null, + recalcWhen: 0, + recalcDeps: null + } + }, + { + id: 'E', + fields: { + colRef: 6, + parentId: 1, + parentPos: 4, + type: 'Any', + widgetOptions: '', + isFormula: true, + formula: '$A.upper()', + label: 'E', + description: '', + untieColIdFromLabel: false, + summarySourceCol: 0, + displayCol: 0, + visibleCol: 0, + rules: null, + recalcWhen: 0, + recalcDeps: null + } + } + ] + }; + const respWithTableId = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/Table1/columns`, chimpy); + assert.equal(respWithTableId.status, 200); + assert.deepEqual(respWithTableId.data, data); + const respWithTableRef = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/1/columns`, chimpy); + assert.equal(respWithTableRef.status, 200); + assert.deepEqual(respWithTableRef.data, data); }); it('GET /docs/{did}/tables/{tid}/columns retrieves hidden columns when "hidden" is set', async function () { @@ -869,6 +886,13 @@ function testDocApi() { ] }); + // POST /columns: Create new columns using tableRef in URL + resp = await axios.post(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/5/columns`, { + columns: [{id: "NewCol6", fields: {}}], + }, chimpy); + assert.equal(resp.status, 200); + assert.deepEqual(resp.data, {columns: [{id: "NewCol6"}]}); + // POST /columns to invalid table ID resp = await axios.post(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/NoSuchTable/columns`, {columns: [{}]}, chimpy); @@ -1032,6 +1056,7 @@ function testDocApi() { {colId: "NewCol4", label: 'NewCol4'}, {colId: "NewCol4_2", label: 'NewCol4_2'}, // NewCol5 is hidden by ACL + {colId: "NewCol6", label: 'NewCol6'}, ]); resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/tables/NewTable2_2/columns`, chimpy);