@budibase/server 2.22.0 → 2.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.22.0",
4
+ "version": "2.22.1",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -48,12 +48,12 @@
48
48
  "license": "GPL-3.0",
49
49
  "dependencies": {
50
50
  "@apidevtools/swagger-parser": "10.0.3",
51
- "@budibase/backend-core": "2.22.0",
52
- "@budibase/client": "2.22.0",
53
- "@budibase/pro": "2.22.0",
54
- "@budibase/shared-core": "2.22.0",
55
- "@budibase/string-templates": "2.22.0",
56
- "@budibase/types": "2.22.0",
51
+ "@budibase/backend-core": "2.22.1",
52
+ "@budibase/client": "2.22.1",
53
+ "@budibase/pro": "2.22.1",
54
+ "@budibase/shared-core": "2.22.1",
55
+ "@budibase/string-templates": "2.22.1",
56
+ "@budibase/types": "2.22.1",
57
57
  "@bull-board/api": "5.10.2",
58
58
  "@bull-board/koa": "5.10.2",
59
59
  "@elastic/elasticsearch": "7.10.0",
@@ -192,5 +192,5 @@
192
192
  }
193
193
  }
194
194
  },
195
- "gitHead": "45f438318a63b0f60cbee8cef5115ca7947f356b"
195
+ "gitHead": "b91392adfadf5e27c40a78102046cb3e51a7a792"
196
196
  }
@@ -16,7 +16,7 @@ import {
16
16
  ViewV2,
17
17
  } from "@budibase/types"
18
18
  import * as setup from "./utilities"
19
- import { mocks } from "@budibase/backend-core/tests"
19
+ import { generator, mocks } from "@budibase/backend-core/tests"
20
20
 
21
21
  const { basicRow } = setup.structures
22
22
  const { BUILTIN_ROLE_IDS } = roles
@@ -44,7 +44,10 @@ describe("/permission", () => {
44
44
 
45
45
  table = (await config.createTable()) as typeof table
46
46
  row = await config.createRow()
47
- view = await config.api.viewV2.create({ tableId: table._id })
47
+ view = await config.api.viewV2.create({
48
+ tableId: table._id!,
49
+ name: generator.guid(),
50
+ })
48
51
  perms = await config.api.permission.add({
49
52
  roleId: STD_ROLE_ID,
50
53
  resourceId: table._id,
@@ -42,6 +42,7 @@ tk.freeze(timestamp)
42
42
  jest.unmock("mysql2")
43
43
  jest.unmock("mysql2/promise")
44
44
  jest.unmock("mssql")
45
+ jest.unmock("pg")
45
46
 
46
47
  describe.each([
47
48
  ["internal", undefined],
@@ -152,8 +153,8 @@ describe.each([
152
153
  table = await config.api.table.save(defaultTable())
153
154
  })
154
155
 
155
- describe("save, load, update", () => {
156
- it("returns a success message when the row is created", async () => {
156
+ describe("create", () => {
157
+ it("creates a new row successfully", async () => {
157
158
  const rowUsage = await getRowUsage()
158
159
  const row = await config.api.row.save(table._id!, {
159
160
  name: "Test Contact",
@@ -163,7 +164,44 @@ describe.each([
163
164
  await assertRowUsage(rowUsage + 1)
164
165
  })
165
166
 
166
- it("Increment row autoId per create row request", async () => {
167
+ it("fails to create a row for a table that does not exist", async () => {
168
+ const rowUsage = await getRowUsage()
169
+ await config.api.row.save("1234567", {}, { status: 404 })
170
+ await assertRowUsage(rowUsage)
171
+ })
172
+
173
+ it("fails to create a row if required fields are missing", async () => {
174
+ const rowUsage = await getRowUsage()
175
+ const table = await config.api.table.save(
176
+ saveTableRequest({
177
+ schema: {
178
+ required: {
179
+ type: FieldType.STRING,
180
+ name: "required",
181
+ constraints: {
182
+ type: "string",
183
+ presence: true,
184
+ },
185
+ },
186
+ },
187
+ })
188
+ )
189
+ await config.api.row.save(
190
+ table._id!,
191
+ {},
192
+ {
193
+ status: 500,
194
+ body: {
195
+ validationErrors: {
196
+ required: ["can't be blank"],
197
+ },
198
+ },
199
+ }
200
+ )
201
+ await assertRowUsage(rowUsage)
202
+ })
203
+
204
+ it("increment row autoId per create row request", async () => {
167
205
  const rowUsage = await getRowUsage()
168
206
 
169
207
  const newTable = await config.api.table.save(
@@ -198,52 +236,6 @@ describe.each([
198
236
  await assertRowUsage(rowUsage + 10)
199
237
  })
200
238
 
201
- it("updates a row successfully", async () => {
202
- const existing = await config.api.row.save(table._id!, {})
203
- const rowUsage = await getRowUsage()
204
-
205
- const res = await config.api.row.save(table._id!, {
206
- _id: existing._id,
207
- _rev: existing._rev,
208
- name: "Updated Name",
209
- })
210
-
211
- expect(res.name).toEqual("Updated Name")
212
- await assertRowUsage(rowUsage)
213
- })
214
-
215
- it("should load a row", async () => {
216
- const existing = await config.api.row.save(table._id!, {})
217
-
218
- const res = await config.api.row.get(table._id!, existing._id!)
219
-
220
- expect(res).toEqual({
221
- ...existing,
222
- ...defaultRowFields,
223
- })
224
- })
225
-
226
- it("should list all rows for given tableId", async () => {
227
- const table = await config.api.table.save(defaultTable())
228
- const rows = await Promise.all([
229
- config.api.row.save(table._id!, {}),
230
- config.api.row.save(table._id!, {}),
231
- ])
232
-
233
- const res = await config.api.row.fetch(table._id!)
234
- expect(res.map(r => r._id)).toEqual(
235
- expect.arrayContaining(rows.map(r => r._id))
236
- )
237
- })
238
-
239
- it("load should return 404 when row does not exist", async () => {
240
- const table = await config.api.table.save(defaultTable())
241
- await config.api.row.save(table._id!, {})
242
- await config.api.row.get(table._id!, "1234567", {
243
- status: 404,
244
- })
245
- })
246
-
247
239
  isInternal &&
248
240
  it("row values are coerced", async () => {
249
241
  const str: FieldSchema = {
@@ -296,8 +288,6 @@ describe.each([
296
288
  }
297
289
  const table = await config.api.table.save(
298
290
  saveTableRequest({
299
- name: "TestTable2",
300
- type: "table",
301
291
  schema: {
302
292
  name: str,
303
293
  stringUndefined: str,
@@ -404,52 +394,59 @@ describe.each([
404
394
  })
405
395
  })
406
396
 
407
- describe("view save", () => {
408
- it("views have extra data trimmed", async () => {
409
- const table = await config.api.table.save(
410
- saveTableRequest({
411
- name: "orders",
412
- schema: {
413
- Country: {
414
- type: FieldType.STRING,
415
- name: "Country",
416
- },
417
- Story: {
418
- type: FieldType.STRING,
419
- name: "Story",
420
- },
421
- },
422
- })
423
- )
397
+ describe("get", () => {
398
+ it("reads an existing row successfully", async () => {
399
+ const existing = await config.api.row.save(table._id!, {})
424
400
 
425
- const createViewResponse = await config.api.viewV2.create({
426
- tableId: table._id!,
427
- name: uuid.v4(),
428
- schema: {
429
- Country: {
430
- visible: true,
431
- },
432
- },
401
+ const res = await config.api.row.get(table._id!, existing._id!)
402
+
403
+ expect(res).toEqual({
404
+ ...existing,
405
+ ...defaultRowFields,
433
406
  })
407
+ })
434
408
 
435
- const createRowResponse = await config.api.row.save(
436
- createViewResponse.id,
437
- {
438
- Country: "Aussy",
439
- Story: "aaaaa",
440
- }
409
+ it("returns 404 when row does not exist", async () => {
410
+ const table = await config.api.table.save(defaultTable())
411
+ await config.api.row.save(table._id!, {})
412
+ await config.api.row.get(table._id!, "1234567", {
413
+ status: 404,
414
+ })
415
+ })
416
+ })
417
+
418
+ describe("fetch", () => {
419
+ it("fetches all rows for given tableId", async () => {
420
+ const table = await config.api.table.save(defaultTable())
421
+ const rows = await Promise.all([
422
+ config.api.row.save(table._id!, {}),
423
+ config.api.row.save(table._id!, {}),
424
+ ])
425
+
426
+ const res = await config.api.row.fetch(table._id!)
427
+ expect(res.map(r => r._id)).toEqual(
428
+ expect.arrayContaining(rows.map(r => r._id))
441
429
  )
430
+ })
442
431
 
443
- const row = await config.api.row.get(table._id!, createRowResponse._id!)
444
- expect(row.Story).toBeUndefined()
445
- expect(row).toEqual({
446
- ...defaultRowFields,
447
- Country: "Aussy",
448
- id: createRowResponse.id,
449
- _id: createRowResponse._id,
450
- _rev: createRowResponse._rev,
451
- tableId: table._id,
432
+ it("returns 404 when table does not exist", async () => {
433
+ await config.api.row.fetch("1234567", { status: 404 })
434
+ })
435
+ })
436
+
437
+ describe("update", () => {
438
+ it("updates an existing row successfully", async () => {
439
+ const existing = await config.api.row.save(table._id!, {})
440
+ const rowUsage = await getRowUsage()
441
+
442
+ const res = await config.api.row.save(table._id!, {
443
+ _id: existing._id,
444
+ _rev: existing._rev,
445
+ name: "Updated Name",
452
446
  })
447
+
448
+ expect(res.name).toEqual("Updated Name")
449
+ await assertRowUsage(rowUsage)
453
450
  })
454
451
  })
455
452
 
@@ -722,50 +719,7 @@ describe.each([
722
719
  })
723
720
  })
724
721
 
725
- // Legacy views are not available for external
726
- isInternal &&
727
- describe("fetchView", () => {
728
- beforeEach(async () => {
729
- table = await config.api.table.save(defaultTable())
730
- })
731
-
732
- it("should be able to fetch tables contents via 'view'", async () => {
733
- const row = await config.api.row.save(table._id!, {})
734
- const rowUsage = await getRowUsage()
735
-
736
- const rows = await config.api.legacyView.get(table._id!)
737
- expect(rows.length).toEqual(1)
738
- expect(rows[0]._id).toEqual(row._id)
739
- await assertRowUsage(rowUsage)
740
- })
741
-
742
- it("should throw an error if view doesn't exist", async () => {
743
- const rowUsage = await getRowUsage()
744
-
745
- await config.api.legacyView.get("derp", undefined, { status: 404 })
746
-
747
- await assertRowUsage(rowUsage)
748
- })
749
-
750
- it("should be able to run on a view", async () => {
751
- const view = await config.createLegacyView({
752
- tableId: table._id!,
753
- name: "ViewTest",
754
- filters: [],
755
- schema: {},
756
- })
757
- const row = await config.api.row.save(table._id!, {})
758
- const rowUsage = await getRowUsage()
759
-
760
- const rows = await config.api.legacyView.get(view.name)
761
- expect(rows.length).toEqual(1)
762
- expect(rows[0]._id).toEqual(row._id)
763
-
764
- await assertRowUsage(rowUsage)
765
- })
766
- })
767
-
768
- describe("fetchEnrichedRows", () => {
722
+ describe("enrich", () => {
769
723
  beforeAll(async () => {
770
724
  table = await config.api.table.save(defaultTable())
771
725
  })
@@ -827,10 +781,6 @@ describe.each([
827
781
 
828
782
  isInternal &&
829
783
  describe("attachments", () => {
830
- beforeAll(async () => {
831
- table = await config.api.table.save(defaultTable())
832
- })
833
-
834
784
  it("should allow enriching attachment rows", async () => {
835
785
  const table = await config.api.table.save(
836
786
  defaultTable({
@@ -865,7 +815,7 @@ describe.each([
865
815
  })
866
816
  })
867
817
 
868
- describe("exportData", () => {
818
+ describe("exportRows", () => {
869
819
  beforeAll(async () => {
870
820
  table = await config.api.table.save(defaultTable())
871
821
  })
@@ -947,6 +897,7 @@ describe.each([
947
897
  const table = await config.api.table.save(await userTable())
948
898
  const view = await config.api.viewV2.create({
949
899
  tableId: table._id!,
900
+ name: generator.guid(),
950
901
  schema: {
951
902
  name: { visible: true },
952
903
  surname: { visible: true },
@@ -984,6 +935,7 @@ describe.each([
984
935
  const tableId = table._id!
985
936
  const view = await config.api.viewV2.create({
986
937
  tableId: tableId,
938
+ name: generator.guid(),
987
939
  schema: {
988
940
  name: { visible: true },
989
941
  address: { visible: true },
@@ -1026,6 +978,7 @@ describe.each([
1026
978
  const tableId = table._id!
1027
979
  const view = await config.api.viewV2.create({
1028
980
  tableId: tableId,
981
+ name: generator.guid(),
1029
982
  schema: {
1030
983
  name: { visible: true },
1031
984
  address: { visible: true },
@@ -1049,6 +1002,7 @@ describe.each([
1049
1002
  const tableId = table._id!
1050
1003
  const view = await config.api.viewV2.create({
1051
1004
  tableId: tableId,
1005
+ name: generator.guid(),
1052
1006
  schema: {
1053
1007
  name: { visible: true },
1054
1008
  address: { visible: true },
@@ -1109,6 +1063,7 @@ describe.each([
1109
1063
 
1110
1064
  const createViewResponse = await config.api.viewV2.create({
1111
1065
  tableId: table._id!,
1066
+ name: generator.guid(),
1112
1067
  })
1113
1068
  const response = await config.api.viewV2.search(createViewResponse.id)
1114
1069
 
@@ -1155,6 +1110,7 @@ describe.each([
1155
1110
 
1156
1111
  const createViewResponse = await config.api.viewV2.create({
1157
1112
  tableId: table._id!,
1113
+ name: generator.guid(),
1158
1114
  query: [
1159
1115
  { operator: SearchQueryOperators.EQUAL, field: "age", value: 40 },
1160
1116
  ],
@@ -1279,6 +1235,7 @@ describe.each([
1279
1235
  async (sortParams, expected) => {
1280
1236
  const createViewResponse = await config.api.viewV2.create({
1281
1237
  tableId: table._id!,
1238
+ name: generator.guid(),
1282
1239
  sort: sortParams,
1283
1240
  schema: viewSchema,
1284
1241
  })
@@ -1299,6 +1256,7 @@ describe.each([
1299
1256
  async (sortParams, expected) => {
1300
1257
  const createViewResponse = await config.api.viewV2.create({
1301
1258
  tableId: table._id!,
1259
+ name: generator.guid(),
1302
1260
  sort: {
1303
1261
  field: "name",
1304
1262
  order: SortOrder.ASCENDING,
@@ -1339,6 +1297,7 @@ describe.each([
1339
1297
 
1340
1298
  const view = await config.api.viewV2.create({
1341
1299
  tableId: table._id!,
1300
+ name: generator.guid(),
1342
1301
  schema: { name: { visible: true } },
1343
1302
  })
1344
1303
  const response = await config.api.viewV2.search(view.id)
@@ -1361,6 +1320,7 @@ describe.each([
1361
1320
  const table = await config.api.table.save(await userTable())
1362
1321
  const createViewResponse = await config.api.viewV2.create({
1363
1322
  tableId: table._id!,
1323
+ name: generator.guid(),
1364
1324
  })
1365
1325
  const response = await config.api.viewV2.search(createViewResponse.id)
1366
1326
  expect(response.rows).toHaveLength(0)
@@ -1376,6 +1336,7 @@ describe.each([
1376
1336
 
1377
1337
  const createViewResponse = await config.api.viewV2.create({
1378
1338
  tableId: table._id!,
1339
+ name: generator.guid(),
1379
1340
  })
1380
1341
  const response = await config.api.viewV2.search(createViewResponse.id, {
1381
1342
  limit,
@@ -1392,6 +1353,7 @@ describe.each([
1392
1353
  )
1393
1354
  const view = await config.api.viewV2.create({
1394
1355
  tableId: table._id!,
1356
+ name: generator.guid(),
1395
1357
  })
1396
1358
  const rows = (await config.api.viewV2.search(view.id)).rows
1397
1359
 
@@ -1466,6 +1428,7 @@ describe.each([
1466
1428
 
1467
1429
  view = await config.api.viewV2.create({
1468
1430
  tableId: table._id!,
1431
+ name: generator.guid(),
1469
1432
  })
1470
1433
  })
1471
1434
 
@@ -20,7 +20,7 @@ import sdk from "../../../sdk"
20
20
  import * as uuid from "uuid"
21
21
 
22
22
  import tk from "timekeeper"
23
- import { mocks } from "@budibase/backend-core/tests"
23
+ import { generator, mocks } from "@budibase/backend-core/tests"
24
24
  import { TableToBuild } from "../../../tests/utilities/TestConfiguration"
25
25
 
26
26
  tk.freeze(mocks.date.MOCK_DATE)
@@ -417,8 +417,8 @@ describe("/tables", () => {
417
417
  it("should fetch views", async () => {
418
418
  const tableId = config.table!._id!
419
419
  const views = [
420
- await config.api.viewV2.create({ tableId }),
421
- await config.api.viewV2.create({ tableId }),
420
+ await config.api.viewV2.create({ tableId, name: generator.guid() }),
421
+ await config.api.viewV2.create({ tableId, name: generator.guid() }),
422
422
  ]
423
423
 
424
424
  const res = await request
@@ -455,7 +455,7 @@ describe("/tables", () => {
455
455
  },
456
456
  }))
457
457
 
458
- await config.api.viewV2.create({ tableId })
458
+ await config.api.viewV2.create({ tableId, name: generator.guid() })
459
459
  await config.createLegacyView()
460
460
 
461
461
  const res = await config.api.table.fetch()
@@ -3,12 +3,15 @@ import * as setup from "./utilities"
3
3
  import {
4
4
  FieldType,
5
5
  INTERNAL_TABLE_SOURCE_ID,
6
+ QuotaUsageType,
6
7
  SaveTableRequest,
8
+ StaticQuotaName,
7
9
  Table,
8
10
  TableSourceType,
9
11
  View,
10
12
  ViewCalculation,
11
13
  } from "@budibase/types"
14
+ import { quotas } from "@budibase/pro"
12
15
 
13
16
  const priceTable: SaveTableRequest = {
14
17
  name: "table",
@@ -57,6 +60,18 @@ describe("/views", () => {
57
60
  return config.api.legacyView.save(viewToSave)
58
61
  }
59
62
 
63
+ const getRowUsage = async () => {
64
+ const { total } = await config.doInContext(undefined, () =>
65
+ quotas.getCurrentUsageValues(QuotaUsageType.STATIC, StaticQuotaName.ROWS)
66
+ )
67
+ return total
68
+ }
69
+
70
+ const assertRowUsage = async (expected: number) => {
71
+ const usage = await getRowUsage()
72
+ expect(usage).toBe(expected)
73
+ }
74
+
60
75
  describe("create", () => {
61
76
  it("returns a success message when the view is successfully created", async () => {
62
77
  const res = await saveView()
@@ -265,6 +280,41 @@ describe("/views", () => {
265
280
  expect(views.length).toBe(1)
266
281
  expect(views.find(({ name }) => name === "TestView")).toBeDefined()
267
282
  })
283
+
284
+ it("should be able to fetch tables contents via 'view'", async () => {
285
+ const row = await config.api.row.save(table._id!, {})
286
+ const rowUsage = await getRowUsage()
287
+
288
+ const rows = await config.api.legacyView.get(table._id!)
289
+ expect(rows.length).toEqual(1)
290
+ expect(rows[0]._id).toEqual(row._id)
291
+ await assertRowUsage(rowUsage)
292
+ })
293
+
294
+ it("should throw an error if view doesn't exist", async () => {
295
+ const rowUsage = await getRowUsage()
296
+
297
+ await config.api.legacyView.get("derp", undefined, { status: 404 })
298
+
299
+ await assertRowUsage(rowUsage)
300
+ })
301
+
302
+ it("should be able to run on a view", async () => {
303
+ const view = await config.api.legacyView.save({
304
+ tableId: table._id!,
305
+ name: "ViewTest",
306
+ filters: [],
307
+ schema: {},
308
+ })
309
+ const row = await config.api.row.save(table._id!, {})
310
+ const rowUsage = await getRowUsage()
311
+
312
+ const rows = await config.api.legacyView.get(view.name!)
313
+ expect(rows.length).toEqual(1)
314
+ expect(rows[0]._id).toEqual(row._id)
315
+
316
+ await assertRowUsage(rowUsage)
317
+ })
268
318
  })
269
319
 
270
320
  describe("query", () => {
@@ -1,9 +1,11 @@
1
1
  import * as setup from "./utilities"
2
2
  import {
3
3
  CreateViewRequest,
4
+ Datasource,
4
5
  FieldSchema,
5
6
  FieldType,
6
7
  INTERNAL_TABLE_SOURCE_ID,
8
+ SaveTableRequest,
7
9
  SearchQueryOperators,
8
10
  SortOrder,
9
11
  SortType,
@@ -14,65 +16,88 @@ import {
14
16
  ViewV2,
15
17
  } from "@budibase/types"
16
18
  import { generator } from "@budibase/backend-core/tests"
17
- import { generateDatasourceID } from "../../../db/utils"
18
-
19
- function priceTable(): Table {
20
- return {
21
- name: "table",
22
- type: "table",
23
- sourceId: INTERNAL_TABLE_SOURCE_ID,
24
- sourceType: TableSourceType.INTERNAL,
25
- schema: {
26
- Price: {
27
- type: FieldType.NUMBER,
28
- name: "Price",
29
- constraints: {},
30
- },
31
- Category: {
32
- type: FieldType.STRING,
33
- name: "Category",
34
- constraints: {
35
- type: "string",
36
- },
37
- },
38
- },
39
- }
40
- }
19
+ import * as uuid from "uuid"
20
+ import { databaseTestProviders } from "../../../integrations/tests/utils"
21
+ import merge from "lodash/merge"
41
22
 
42
- const config = setup.getConfig()
43
-
44
- beforeAll(async () => {
45
- await config.init()
46
- })
23
+ jest.unmock("mysql2")
24
+ jest.unmock("mysql2/promise")
25
+ jest.unmock("mssql")
26
+ jest.unmock("pg")
47
27
 
48
28
  describe.each([
49
- ["internal ds", () => config.createTable(priceTable())],
50
- [
51
- "external ds",
52
- async () => {
53
- const datasource = await config.createDatasource({
54
- datasource: {
55
- ...setup.structures.basicDatasource().datasource,
56
- plus: true,
57
- _id: generateDatasourceID({ plus: true }),
58
- },
59
- })
29
+ ["internal", undefined],
30
+ ["postgres", databaseTestProviders.postgres],
31
+ ["mysql", databaseTestProviders.mysql],
32
+ ["mssql", databaseTestProviders.mssql],
33
+ ["mariadb", databaseTestProviders.mariadb],
34
+ ])("/v2/views (%s)", (_, dsProvider) => {
35
+ const config = setup.getConfig()
60
36
 
61
- return config.createExternalTable({
62
- ...priceTable(),
63
- sourceId: datasource._id,
64
- sourceType: TableSourceType.EXTERNAL,
65
- })
66
- },
67
- ],
68
- ])("/v2/views (%s)", (_, tableBuilder) => {
69
37
  let table: Table
38
+ let datasource: Datasource
39
+
40
+ function saveTableRequest(
41
+ ...overrides: Partial<SaveTableRequest>[]
42
+ ): SaveTableRequest {
43
+ const req: SaveTableRequest = {
44
+ name: uuid.v4().substring(0, 16),
45
+ type: "table",
46
+ sourceType: datasource
47
+ ? TableSourceType.EXTERNAL
48
+ : TableSourceType.INTERNAL,
49
+ sourceId: datasource ? datasource._id! : INTERNAL_TABLE_SOURCE_ID,
50
+ primary: ["id"],
51
+ schema: {
52
+ id: {
53
+ type: FieldType.AUTO,
54
+ name: "id",
55
+ autocolumn: true,
56
+ constraints: {
57
+ presence: true,
58
+ },
59
+ },
60
+ },
61
+ }
62
+ return merge(req, ...overrides)
63
+ }
64
+
65
+ function priceTable(): SaveTableRequest {
66
+ return saveTableRequest({
67
+ schema: {
68
+ Price: {
69
+ type: FieldType.NUMBER,
70
+ name: "Price",
71
+ constraints: {},
72
+ },
73
+ Category: {
74
+ type: FieldType.STRING,
75
+ name: "Category",
76
+ constraints: {
77
+ type: "string",
78
+ },
79
+ },
80
+ },
81
+ })
82
+ }
70
83
 
71
84
  beforeAll(async () => {
72
- table = await tableBuilder()
85
+ await config.init()
86
+
87
+ if (dsProvider) {
88
+ datasource = await config.createDatasource({
89
+ datasource: await dsProvider.datasource(),
90
+ })
91
+ }
92
+ table = await config.api.table.save(priceTable())
73
93
  })
74
94
 
75
- afterAll(setup.afterAll)
95
+ afterAll(async () => {
96
+ if (dsProvider) {
97
+ await dsProvider.stop()
98
+ }
99
+ setup.afterAll()
100
+ })
76
101
 
77
102
  describe("create", () => {
78
103
  it("persist the view when the view is successfully created", async () => {
@@ -186,9 +211,12 @@ describe.each([
186
211
  let view: ViewV2
187
212
 
188
213
  beforeEach(async () => {
189
- table = await tableBuilder()
214
+ table = await config.api.table.save(priceTable())
190
215
 
191
- view = await config.api.viewV2.create({ name: "View A" })
216
+ view = await config.api.viewV2.create({
217
+ tableId: table._id!,
218
+ name: "View A",
219
+ })
192
220
  })
193
221
 
194
222
  it("can update an existing view data", async () => {
@@ -247,6 +275,9 @@ describe.each([
247
275
  ...updatedData,
248
276
  schema: {
249
277
  ...table.schema,
278
+ id: expect.objectContaining({
279
+ visible: false,
280
+ }),
250
281
  Category: expect.objectContaining({
251
282
  visible: false,
252
283
  }),
@@ -320,23 +351,27 @@ describe.each([
320
351
  })
321
352
 
322
353
  it("cannot update views v1", async () => {
323
- const viewV1 = await config.createLegacyView()
324
- await config.api.viewV2.update(
325
- {
326
- ...viewV1,
327
- },
328
- {
354
+ const viewV1 = await config.api.legacyView.save({
355
+ tableId: table._id!,
356
+ name: generator.guid(),
357
+ filters: [],
358
+ schema: {},
359
+ })
360
+
361
+ await config.api.viewV2.update(viewV1 as unknown as ViewV2, {
362
+ status: 400,
363
+ body: {
364
+ message: "Only views V2 can be updated",
329
365
  status: 400,
330
- body: {
331
- message: "Only views V2 can be updated",
332
- status: 400,
333
- },
334
- }
335
- )
366
+ },
367
+ })
336
368
  })
337
369
 
338
370
  it("cannot update the a view with unmatching ids between url and body", async () => {
339
- const anotherView = await config.api.viewV2.create()
371
+ const anotherView = await config.api.viewV2.create({
372
+ tableId: table._id!,
373
+ name: generator.guid(),
374
+ })
340
375
  const result = await config
341
376
  .request!.put(`/api/v2/views/${anotherView.id}`)
342
377
  .send(view)
@@ -411,7 +446,10 @@ describe.each([
411
446
  let view: ViewV2
412
447
 
413
448
  beforeAll(async () => {
414
- view = await config.api.viewV2.create()
449
+ view = await config.api.viewV2.create({
450
+ tableId: table._id!,
451
+ name: generator.guid(),
452
+ })
415
453
  })
416
454
 
417
455
  it("can delete an existing view", async () => {
@@ -448,4 +486,43 @@ describe.each([
448
486
  expect(viewSchema.Price?.visible).toEqual(false)
449
487
  })
450
488
  })
489
+
490
+ describe("read", () => {
491
+ it("views have extra data trimmed", async () => {
492
+ const table = await config.api.table.save(
493
+ saveTableRequest({
494
+ name: "orders",
495
+ schema: {
496
+ Country: {
497
+ type: FieldType.STRING,
498
+ name: "Country",
499
+ },
500
+ Story: {
501
+ type: FieldType.STRING,
502
+ name: "Story",
503
+ },
504
+ },
505
+ })
506
+ )
507
+
508
+ const view = await config.api.viewV2.create({
509
+ tableId: table._id!,
510
+ name: uuid.v4(),
511
+ schema: {
512
+ Country: {
513
+ visible: true,
514
+ },
515
+ },
516
+ })
517
+
518
+ let row = await config.api.row.save(view.id, {
519
+ Country: "Aussy",
520
+ Story: "aaaaa",
521
+ })
522
+
523
+ row = await config.api.row.get(table._id!, row._id!)
524
+ expect(row.Story).toBeUndefined()
525
+ expect(row.Country).toEqual("Aussy")
526
+ })
527
+ })
451
528
  })
@@ -11,21 +11,9 @@ import sdk from "../../../sdk"
11
11
 
12
12
  export class ViewV2API extends TestAPI {
13
13
  create = async (
14
- viewData?: Partial<CreateViewRequest>,
14
+ view: CreateViewRequest,
15
15
  expectations?: Expectations
16
16
  ): Promise<ViewV2> => {
17
- let tableId = viewData?.tableId
18
- if (!tableId && !this.config.table) {
19
- throw "Test requires table to be configured."
20
- }
21
-
22
- tableId = tableId || this.config.table!._id!
23
- const view = {
24
- tableId,
25
- name: generator.guid(),
26
- ...viewData,
27
- }
28
-
29
17
  const exp: Expectations = {
30
18
  status: 201,
31
19
  ...expectations,
@@ -1,21 +0,0 @@
1
- import * as search from "../../app/rows/search"
2
-
3
- describe("removeEmptyFilters", () => {
4
- it("0 should not be removed", () => {
5
- const filters = search.removeEmptyFilters({
6
- equal: {
7
- column: 0,
8
- },
9
- })
10
- expect((filters.equal as any).column).toBe(0)
11
- })
12
-
13
- it("empty string should be removed", () => {
14
- const filters = search.removeEmptyFilters({
15
- equal: {
16
- column: "",
17
- },
18
- })
19
- expect(Object.values(filters.equal as any).length).toBe(0)
20
- })
21
- })