(core) updates from grist-core

This commit is contained in:
Paul Fitzpatrick
2023-10-17 06:47:46 +02:00
15 changed files with 800 additions and 344 deletions

View File

@@ -230,6 +230,14 @@ describe('DocApi', function () {
// Contains the tests. This is where you want to add more test.
function testDocApi() {
async function generateDocAndUrl(docName: string = "Dummy") {
const wid = (await userApi.getOrgWorkspaces('current')).find((w) => w.name === 'Private')!.id;
const docId = await userApi.newDoc({name: docName}, wid);
const docUrl = `${serverUrl}/api/docs/${docId}`;
const tableUrl = `${serverUrl}/api/docs/${docId}/tables/Table1`;
return { docUrl, tableUrl, docId };
}
it("creator should be owner of a created ws", async () => {
const kiwiEmail = 'kiwi@getgrist.com';
const ws1 = (await userApi.getOrgWorkspaces('current'))[0].id;
@@ -547,9 +555,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 +563,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 +639,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 +706,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 +894,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 +1064,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);
@@ -1055,13 +1088,13 @@ function testDocApi() {
});
describe("/docs/{did}/tables/{tid}/columns", function () {
async function generateDocAndUrl(docName: string = "Dummy") {
const wid = (await userApi.getOrgWorkspaces('current')).find((w) => w.name === 'Private')!.id;
const docId = await userApi.newDoc({name: docName}, wid);
const url = `${serverUrl}/api/docs/${docId}/tables/Table1/columns`;
return { url, docId };
async function generateDocAndUrlForColumns(name: string) {
const { tableUrl, docId } = await generateDocAndUrl(name);
return {
docId,
url: `${tableUrl}/columns`,
};
}
describe("PUT /docs/{did}/tables/{tid}/columns", function () {
async function getColumnFieldsMapById(url: string, params: any) {
const result = await axios.get(url, {...chimpy, params});
@@ -1079,7 +1112,7 @@ function testDocApi() {
expectedFieldsByColId: Record<string, object>,
opts?: { getParams?: any }
) {
const {url} = await generateDocAndUrl('ColumnsPut');
const {url} = await generateDocAndUrlForColumns('ColumnsPut');
const body: ColumnsPut = { columns };
const resp = await axios.put(url, body, {...chimpy, params});
assert.equal(resp.status, 200);
@@ -1150,7 +1183,7 @@ function testDocApi() {
it('should forbid update by viewers', async function () {
// given
const { url, docId } = await generateDocAndUrl('ColumnsPut');
const { url, docId } = await generateDocAndUrlForColumns('ColumnsPut');
await userApi.updateDocPermissions(docId, {users: {'kiwi@getgrist.com': 'viewers'}});
// when
@@ -1162,7 +1195,7 @@ function testDocApi() {
it("should return 404 when table is not found", async function() {
// given
const { url } = await generateDocAndUrl('ColumnsPut');
const { url } = await generateDocAndUrlForColumns('ColumnsPut');
const notFoundUrl = url.replace("Table1", "NonExistingTable");
// when
@@ -1176,7 +1209,7 @@ function testDocApi() {
describe("DELETE /docs/{did}/tables/{tid}/columns/{colId}", function () {
it('should delete some column', async function() {
const {url} = await generateDocAndUrl('ColumnDelete');
const {url} = await generateDocAndUrlForColumns('ColumnDelete');
const deleteUrl = url + '/A';
const resp = await axios.delete(deleteUrl, chimpy);
@@ -1190,7 +1223,7 @@ function testDocApi() {
});
it('should return 404 if table not found', async function() {
const {url} = await generateDocAndUrl('ColumnDelete');
const {url} = await generateDocAndUrlForColumns('ColumnDelete');
const deleteUrl = url.replace("Table1", "NonExistingTable") + '/A';
const resp = await axios.delete(deleteUrl, chimpy);
@@ -1199,7 +1232,7 @@ function testDocApi() {
});
it('should return 404 if column not found', async function() {
const {url} = await generateDocAndUrl('ColumnDelete');
const {url} = await generateDocAndUrlForColumns('ColumnDelete');
const deleteUrl = url + '/NonExistingColId';
const resp = await axios.delete(deleteUrl, chimpy);
@@ -1208,7 +1241,7 @@ function testDocApi() {
});
it('should forbid column deletion by viewers', async function() {
const {url, docId} = await generateDocAndUrl('ColumnDelete');
const {url, docId} = await generateDocAndUrlForColumns('ColumnDelete');
await userApi.updateDocPermissions(docId, {users: {'kiwi@getgrist.com': 'viewers'}});
const deleteUrl = url + '/A';
const resp = await axios.delete(deleteUrl, kiwi);
@@ -2584,6 +2617,25 @@ function testDocApi() {
assert.equal(resp2.data, 'A,B\nSanta,1\nBob,11\nAlice,2\nFelix,22\n');
});
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;
const userActions = [
['AddRecord', 'Table1', null, {A: 'a1', B: 'b1'}],
['UpdateRecord', '_grist_Tables_column', AColRef, { untieColIdFromLabel: true }],
['UpdateRecord', '_grist_Tables_column', AColRef, {
label: 'Column label for A',
colId: 'AColId'
}]
];
const resp = await axios.post(`${docUrl}/apply`, userActions, chimpy);
assert.equal(resp.status, 200);
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');
});
it("GET /docs/{did}/download/csv respects permissions", async function () {
// kiwi has no access to TestDoc
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.TestDoc}/download/csv?tableId=Table1`, kiwi);