|
|
@ -32,7 +32,7 @@ import {
|
|
|
|
TableOperationsImpl,
|
|
|
|
TableOperationsImpl,
|
|
|
|
TableOperationsPlatform
|
|
|
|
TableOperationsPlatform
|
|
|
|
} from 'app/plugin/TableOperationsImpl';
|
|
|
|
} 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 {appSettings} from "app/server/lib/AppSettings";
|
|
|
|
import {sendForCompletion} from 'app/server/lib/Assistance';
|
|
|
|
import {sendForCompletion} from 'app/server/lib/Assistance';
|
|
|
|
import {
|
|
|
|
import {
|
|
|
@ -201,7 +201,7 @@ export class DocWorkerApi {
|
|
|
|
if (!Object.keys(filters).every(col => Array.isArray(filters[col]))) {
|
|
|
|
if (!Object.keys(filters).every(col => Array.isArray(filters[col]))) {
|
|
|
|
throw new ApiError("Invalid query: filter values must be arrays", 400);
|
|
|
|
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 session = docSessionFromRequest(req);
|
|
|
|
const {tableData} = await handleSandboxError(tableId, [], activeDoc.fetchQuery(
|
|
|
|
const {tableData} = await handleSandboxError(tableId, [], activeDoc.fetchQuery(
|
|
|
|
session, {tableId, filters}, !immediate));
|
|
|
|
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 registerWebhook = async (activeDoc: ActiveDoc, req: RequestWithLogin, webhook: WebhookFields) => {
|
|
|
|
const {fields, url} = await getWebhookSettings(activeDoc, req, null, webhook);
|
|
|
|
const {fields, url} = await getWebhookSettings(activeDoc, req, null, webhook);
|
|
|
|
if (!fields.eventTypes?.length) {
|
|
|
|
if (!fields.eventTypes?.length) {
|
|
|
@ -337,7 +332,8 @@ export class DocWorkerApi {
|
|
|
|
const trigger = webhookId ? activeDoc.triggers.getWebhookTriggerRecord(webhookId) : undefined;
|
|
|
|
const trigger = webhookId ? activeDoc.triggers.getWebhookTriggerRecord(webhookId) : undefined;
|
|
|
|
let currentTableId = trigger ? tablesTable.getValue(trigger.tableRef, 'tableId')! : undefined;
|
|
|
|
let currentTableId = trigger ? tablesTable.getValue(trigger.tableRef, 'tableId')! : undefined;
|
|
|
|
const {url, eventTypes, isReadyColumn, name} = webhook;
|
|
|
|
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']> = {};
|
|
|
|
const fields: Partial<SchemaTypes['_grist_Triggers']> = {};
|
|
|
|
|
|
|
|
|
|
|
|
if (url && !isUrlAllowed(url)) {
|
|
|
|
if (url && !isUrlAllowed(url)) {
|
|
|
@ -387,7 +383,7 @@ export class DocWorkerApi {
|
|
|
|
// Get the columns of the specified table in recordish format
|
|
|
|
// Get the columns of the specified table in recordish format
|
|
|
|
this._app.get('/api/docs/:docId/tables/:tableId/columns', canView,
|
|
|
|
this._app.get('/api/docs/:docId/tables/:tableId/columns', canView,
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
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 includeHidden = isAffirmative(req.query.hidden);
|
|
|
|
const columns = await handleSandboxError('', [],
|
|
|
|
const columns = await handleSandboxError('', [],
|
|
|
|
activeDoc.getTableCols(docSessionFromRequest(req), tableId, includeHidden));
|
|
|
|
activeDoc.getTableCols(docSessionFromRequest(req), tableId, includeHidden));
|
|
|
@ -498,7 +494,7 @@ export class DocWorkerApi {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
const colValues = req.body as BulkColValues;
|
|
|
|
const colValues = req.body as BulkColValues;
|
|
|
|
const count = colValues[Object.keys(colValues)[0]].length;
|
|
|
|
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);
|
|
|
|
const ids = await op.addRecords(count, colValues);
|
|
|
|
res.json(ids);
|
|
|
|
res.json(ids);
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -527,7 +523,7 @@ export class DocWorkerApi {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
validateCore(RecordsPost, req, body);
|
|
|
|
validateCore(RecordsPost, req, body);
|
|
|
|
const ops = getTableOperations(req, activeDoc);
|
|
|
|
const ops = await getTableOperations(req, activeDoc);
|
|
|
|
const records = await ops.create(body.records);
|
|
|
|
const records = await ops.create(body.records);
|
|
|
|
res.json({records});
|
|
|
|
res.json({records});
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -558,7 +554,7 @@ export class DocWorkerApi {
|
|
|
|
this._app.post('/api/docs/:docId/tables/:tableId/columns', canEdit, validate(ColumnsPost),
|
|
|
|
this._app.post('/api/docs/:docId/tables/:tableId/columns', canEdit, validate(ColumnsPost),
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
const body = req.body as Types.ColumnsPost;
|
|
|
|
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}) =>
|
|
|
|
const actions = body.columns.map(({fields, id: colId}) =>
|
|
|
|
// AddVisibleColumn adds the column to all widgets of the table.
|
|
|
|
// 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.
|
|
|
|
// 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) => {
|
|
|
|
this._app.post('/api/docs/:docId/tables/:tableId/data/delete', canEdit, withDoc(async (activeDoc, req, res) => {
|
|
|
|
const rowIds = req.body;
|
|
|
|
const rowIds = req.body;
|
|
|
|
const op = getTableOperations(req, activeDoc);
|
|
|
|
const op = await getTableOperations(req, activeDoc);
|
|
|
|
await op.destroy(rowIds);
|
|
|
|
await op.destroy(rowIds);
|
|
|
|
res.json(null);
|
|
|
|
res.json(null);
|
|
|
|
}));
|
|
|
|
}));
|
|
|
@ -659,7 +655,7 @@ export class DocWorkerApi {
|
|
|
|
const rowIds = columnValues.id;
|
|
|
|
const rowIds = columnValues.id;
|
|
|
|
// sandbox expects no id column
|
|
|
|
// sandbox expects no id column
|
|
|
|
delete columnValues.id;
|
|
|
|
delete columnValues.id;
|
|
|
|
const ops = getTableOperations(req, activeDoc);
|
|
|
|
const ops = await getTableOperations(req, activeDoc);
|
|
|
|
await ops.updateRecords(columnValues, rowIds);
|
|
|
|
await ops.updateRecords(columnValues, rowIds);
|
|
|
|
res.json(null);
|
|
|
|
res.json(null);
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -669,7 +665,7 @@ export class DocWorkerApi {
|
|
|
|
this._app.patch('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPatch),
|
|
|
|
this._app.patch('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPatch),
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
const body = req.body as Types.RecordsPatch;
|
|
|
|
const body = req.body as Types.RecordsPatch;
|
|
|
|
const ops = getTableOperations(req, activeDoc);
|
|
|
|
const ops = await getTableOperations(req, activeDoc);
|
|
|
|
await ops.update(body.records);
|
|
|
|
await ops.update(body.records);
|
|
|
|
res.json(null);
|
|
|
|
res.json(null);
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -680,7 +676,7 @@ export class DocWorkerApi {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
|
|
|
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
|
|
|
const columnsTable = activeDoc.docData!.getMetaTable("_grist_Tables_column");
|
|
|
|
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});
|
|
|
|
const tableRef = tablesTable.findMatchingRowId({tableId});
|
|
|
|
if (!tableRef) {
|
|
|
|
if (!tableRef) {
|
|
|
|
throw new ApiError(`Table not found "${tableId}"`, 404);
|
|
|
|
throw new ApiError(`Table not found "${tableId}"`, 404);
|
|
|
@ -693,7 +689,7 @@ export class DocWorkerApi {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {...col, id};
|
|
|
|
return {...col, id};
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const ops = getTableOperations(req, activeDoc, "_grist_Tables_column");
|
|
|
|
const ops = await getTableOperations(req, activeDoc, "_grist_Tables_column");
|
|
|
|
await ops.update(columns);
|
|
|
|
await ops.update(columns);
|
|
|
|
res.json(null);
|
|
|
|
res.json(null);
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -711,7 +707,7 @@ export class DocWorkerApi {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {...table, id};
|
|
|
|
return {...table, id};
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const ops = getTableOperations(req, activeDoc, "_grist_Tables");
|
|
|
|
const ops = await getTableOperations(req, activeDoc, "_grist_Tables");
|
|
|
|
await ops.update(tables);
|
|
|
|
await ops.update(tables);
|
|
|
|
res.json(null);
|
|
|
|
res.json(null);
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -720,7 +716,7 @@ export class DocWorkerApi {
|
|
|
|
// Add or update records given in records format
|
|
|
|
// Add or update records given in records format
|
|
|
|
this._app.put('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPut),
|
|
|
|
this._app.put('/api/docs/:docId/tables/:tableId/records', canEdit, validate(RecordsPut),
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
const ops = getTableOperations(req, activeDoc);
|
|
|
|
const ops = await getTableOperations(req, activeDoc);
|
|
|
|
const body = req.body as Types.RecordsPut;
|
|
|
|
const body = req.body as Types.RecordsPut;
|
|
|
|
const options = {
|
|
|
|
const options = {
|
|
|
|
add: !isAffirmative(req.query.noadd),
|
|
|
|
add: !isAffirmative(req.query.noadd),
|
|
|
@ -740,7 +736,7 @@ export class DocWorkerApi {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
|
|
|
const tablesTable = activeDoc.docData!.getMetaTable("_grist_Tables");
|
|
|
|
const columnsTable = activeDoc.docData!.getMetaTable("_grist_Tables_column");
|
|
|
|
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});
|
|
|
|
const tableRef = tablesTable.findMatchingRowId({tableId});
|
|
|
|
if (!tableRef) {
|
|
|
|
if (!tableRef) {
|
|
|
|
throw new ApiError(`Table not found "${tableId}"`, 404);
|
|
|
|
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,
|
|
|
|
this._app.delete('/api/docs/:docId/tables/:tableId/columns/:colId', canEdit,
|
|
|
|
withDoc(async (activeDoc, req, res) => {
|
|
|
|
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 ] ];
|
|
|
|
const actions = [ [ 'RemoveColumn', tableId, colId ] ];
|
|
|
|
await handleSandboxError(tableId, [colId],
|
|
|
|
await handleSandboxError(tableId, [colId],
|
|
|
|
activeDoc.applyUserActions(docSessionFromRequest(req), actions)
|
|
|
|
activeDoc.applyUserActions(docSessionFromRequest(req), actions)
|
|
|
@ -1941,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<TableOperationsImpl> {
|
|
|
|
const options: OpOptions = {
|
|
|
|
const options: OpOptions = {
|
|
|
|
parseStrings: !isAffirmative(req.query.noparse)
|
|
|
|
parseStrings: !isAffirmative(req.query.noparse)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const realTableId = await getRealTableId(tableId ?? req.params.tableId, {activeDoc, req});
|
|
|
|
const platform: TableOperationsPlatform = {
|
|
|
|
const platform: TableOperationsPlatform = {
|
|
|
|
...getErrorPlatform(tableId ?? req.params.tableId),
|
|
|
|
...getErrorPlatform(realTableId),
|
|
|
|
applyUserActions(actions, opts) {
|
|
|
|
applyUserActions(actions, opts) {
|
|
|
|
if (!activeDoc) { throw new Error('no document'); }
|
|
|
|
if (!activeDoc) { throw new Error('no document'); }
|
|
|
|
return activeDoc.applyUserActions(
|
|
|
|
return activeDoc.applyUserActions(
|
|
|
|