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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user