@budibase/server 2.6.19-alpha.41 → 2.6.19-alpha.42

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.6.19-alpha.41",
4
+ "version": "2.6.19-alpha.42",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -46,12 +46,12 @@
46
46
  "license": "GPL-3.0",
47
47
  "dependencies": {
48
48
  "@apidevtools/swagger-parser": "10.0.3",
49
- "@budibase/backend-core": "2.6.19-alpha.41",
50
- "@budibase/client": "2.6.19-alpha.41",
51
- "@budibase/pro": "2.6.19-alpha.41",
52
- "@budibase/shared-core": "2.6.19-alpha.41",
53
- "@budibase/string-templates": "2.6.19-alpha.41",
54
- "@budibase/types": "2.6.19-alpha.41",
49
+ "@budibase/backend-core": "2.6.19-alpha.42",
50
+ "@budibase/client": "2.6.19-alpha.42",
51
+ "@budibase/pro": "2.6.19-alpha.42",
52
+ "@budibase/shared-core": "2.6.19-alpha.42",
53
+ "@budibase/string-templates": "2.6.19-alpha.42",
54
+ "@budibase/types": "2.6.19-alpha.42",
55
55
  "@bull-board/api": "3.7.0",
56
56
  "@bull-board/koa": "3.9.4",
57
57
  "@elastic/elasticsearch": "7.10.0",
@@ -80,6 +80,7 @@
80
80
  "global-agent": "3.0.0",
81
81
  "google-auth-library": "7.12.0",
82
82
  "google-spreadsheet": "3.2.0",
83
+ "ioredis": "5.3.2",
83
84
  "jimp": "0.16.1",
84
85
  "joi": "17.6.0",
85
86
  "js-yaml": "4.1.0",
@@ -137,7 +138,6 @@
137
138
  "@types/bson": "4.2.0",
138
139
  "@types/global-agent": "2.1.1",
139
140
  "@types/google-spreadsheet": "3.1.5",
140
- "@types/ioredis": "4.28.10",
141
141
  "@types/jest": "29.5.0",
142
142
  "@types/koa": "2.13.4",
143
143
  "@types/koa__router": "8.0.8",
@@ -157,7 +157,6 @@
157
157
  "copyfiles": "2.4.1",
158
158
  "docker-compose": "0.23.17",
159
159
  "eslint": "6.8.0",
160
- "ioredis-mock": "7.2.0",
161
160
  "is-wsl": "2.2.0",
162
161
  "jest": "29.5.0",
163
162
  "jest-openapi": "0.14.2",
@@ -196,5 +195,5 @@
196
195
  }
197
196
  }
198
197
  },
199
- "gitHead": "b45483f4b33be53615d8e5d1e3df8915f06bf0dd"
198
+ "gitHead": "2f80b0b4cc54ed4f18a711651ae262b85ad88906"
200
199
  }
@@ -21,6 +21,7 @@ import {
21
21
  CreateDatasourceRequest,
22
22
  VerifyDatasourceRequest,
23
23
  VerifyDatasourceResponse,
24
+ FetchDatasourceInfoRequest,
24
25
  FetchDatasourceInfoResponse,
25
26
  IntegrationBase,
26
27
  DatasourcePlus,
@@ -57,6 +58,21 @@ async function getConnector(
57
58
  return new Connector(datasource.config)
58
59
  }
59
60
 
61
+ async function getAndMergeDatasource(datasource: Datasource) {
62
+ let existingDatasource: undefined | Datasource
63
+ if (datasource._id) {
64
+ existingDatasource = await sdk.datasources.get(datasource._id)
65
+ }
66
+ let enrichedDatasource = datasource
67
+ if (existingDatasource) {
68
+ enrichedDatasource = sdk.datasources.mergeConfigs(
69
+ datasource,
70
+ existingDatasource
71
+ )
72
+ }
73
+ return await sdk.datasources.enrich(enrichedDatasource)
74
+ }
75
+
60
76
  async function buildSchemaHelper(datasource: Datasource) {
61
77
  const connector = (await getConnector(datasource)) as DatasourcePlus
62
78
  await connector.buildSchema(datasource._id!, datasource.entities!)
@@ -132,17 +148,7 @@ export async function verify(
132
148
  ctx: UserCtx<VerifyDatasourceRequest, VerifyDatasourceResponse>
133
149
  ) {
134
150
  const { datasource } = ctx.request.body
135
- let existingDatasource: undefined | Datasource
136
- if (datasource._id) {
137
- existingDatasource = await sdk.datasources.get(datasource._id)
138
- }
139
- let enrichedDatasource = datasource
140
- if (existingDatasource) {
141
- enrichedDatasource = sdk.datasources.mergeConfigs(
142
- datasource,
143
- existingDatasource
144
- )
145
- }
151
+ const enrichedDatasource = await getAndMergeDatasource(datasource)
146
152
  const connector = await getConnector(enrichedDatasource)
147
153
  if (!connector.testConnection) {
148
154
  ctx.throw(400, "Connection information verification not supported")
@@ -156,11 +162,11 @@ export async function verify(
156
162
  }
157
163
 
158
164
  export async function information(
159
- ctx: UserCtx<void, FetchDatasourceInfoResponse>
165
+ ctx: UserCtx<FetchDatasourceInfoRequest, FetchDatasourceInfoResponse>
160
166
  ) {
161
- const datasourceId = ctx.params.datasourceId
162
- const datasource = await sdk.datasources.get(datasourceId, { enriched: true })
163
- const connector = (await getConnector(datasource)) as DatasourcePlus
167
+ const { datasource } = ctx.request.body
168
+ const enrichedDatasource = await getAndMergeDatasource(datasource)
169
+ const connector = (await getConnector(enrichedDatasource)) as DatasourcePlus
164
170
  if (!connector.getTableNames) {
165
171
  ctx.throw(400, "Table name fetching not supported by datasource")
166
172
  }
@@ -297,7 +303,7 @@ export async function update(ctx: UserCtx<any, UpdateDatasourceResponse>) {
297
303
  ctx.body = {
298
304
  datasource: await sdk.datasources.removeSecretSingle(datasource),
299
305
  }
300
- builderSocket.emitDatasourceUpdate(ctx, datasource)
306
+ builderSocket?.emitDatasourceUpdate(ctx, datasource)
301
307
  }
302
308
 
303
309
  export async function save(
@@ -340,7 +346,7 @@ export async function save(
340
346
  response.error = schemaError
341
347
  }
342
348
  ctx.body = response
343
- builderSocket.emitDatasourceUpdate(ctx, datasource)
349
+ builderSocket?.emitDatasourceUpdate(ctx, datasource)
344
350
  }
345
351
 
346
352
  async function destroyInternalTablesBySourceId(datasourceId: string) {
@@ -400,7 +406,7 @@ export async function destroy(ctx: UserCtx) {
400
406
 
401
407
  ctx.message = `Datasource deleted.`
402
408
  ctx.status = 200
403
- builderSocket.emitDatasourceDeletion(ctx, datasourceId)
409
+ builderSocket?.emitDatasourceDeletion(ctx, datasourceId)
404
410
  }
405
411
 
406
412
  export async function find(ctx: UserCtx) {
@@ -71,7 +71,7 @@ export async function create(ctx: any) {
71
71
 
72
72
  const doc = await pro.plugins.storePlugin(metadata, directory, source)
73
73
 
74
- clientAppSocket.emit("plugins-update", { name, hash: doc.hash })
74
+ clientAppSocket?.emit("plugins-update", { name, hash: doc.hash })
75
75
  ctx.body = {
76
76
  message: "Plugin uploaded successfully",
77
77
  plugins: [doc],
@@ -78,7 +78,7 @@ export async function save(ctx: UserCtx) {
78
78
  ctx.eventEmitter &&
79
79
  ctx.eventEmitter.emitTable(`table:save`, appId, savedTable)
80
80
  ctx.body = savedTable
81
- builderSocket.emitTableUpdate(ctx, savedTable)
81
+ builderSocket?.emitTableUpdate(ctx, savedTable)
82
82
  }
83
83
 
84
84
  export async function destroy(ctx: UserCtx) {
@@ -91,7 +91,7 @@ export async function destroy(ctx: UserCtx) {
91
91
  ctx.status = 200
92
92
  ctx.table = deletedTable
93
93
  ctx.body = { message: `Table ${tableId} deleted.` }
94
- builderSocket.emitTableDeletion(ctx, tableId)
94
+ builderSocket?.emitTableDeletion(ctx, tableId)
95
95
  }
96
96
 
97
97
  export async function bulkImport(ctx: UserCtx) {
@@ -58,7 +58,7 @@ export async function save(ctx: Ctx) {
58
58
  await handleViewEvents(existingTable.views[viewName], table.views[viewName])
59
59
 
60
60
  ctx.body = table.views[viewName]
61
- builderSocket.emitTableUpdate(ctx, table)
61
+ builderSocket?.emitTableUpdate(ctx, table)
62
62
  }
63
63
 
64
64
  export async function calculationEvents(existingView: View, newView: View) {
@@ -127,7 +127,7 @@ export async function destroy(ctx: Ctx) {
127
127
  await events.view.deleted(view)
128
128
 
129
129
  ctx.body = view
130
- builderSocket.emitTableUpdate(ctx, table)
130
+ builderSocket?.emitTableUpdate(ctx, table)
131
131
  }
132
132
 
133
133
  export async function exportView(ctx: Ctx) {
@@ -20,8 +20,8 @@ router
20
20
  authorized(permissions.BUILDER),
21
21
  datasourceController.verify
22
22
  )
23
- .get(
24
- "/api/datasources/:datasourceId/info",
23
+ .post(
24
+ "/api/datasources/info",
25
25
  authorized(permissions.BUILDER),
26
26
  datasourceController.information
27
27
  )
@@ -114,8 +114,8 @@ describe("/views", () => {
114
114
  expect(res.body.tableId).toBe(table._id)
115
115
 
116
116
  const updatedTable = await config.getTable(table._id)
117
- expect(updatedTable.views).toEqual({
118
- TestView: {
117
+ const expectedObj = expect.objectContaining({
118
+ TestView: expect.objectContaining({
119
119
  field: "Price",
120
120
  calculation: "stats",
121
121
  tableId: table._id,
@@ -143,8 +143,9 @@ describe("/views", () => {
143
143
  type: "string",
144
144
  },
145
145
  },
146
- },
146
+ }),
147
147
  })
148
+ expect(updatedTable.views).toEqual(expectedObj)
148
149
  })
149
150
  })
150
151
 
@@ -25,6 +25,7 @@ const config = setup.getConfig()!
25
25
  jest.setTimeout(30000)
26
26
 
27
27
  jest.unmock("pg")
28
+ jest.mock("../websockets")
28
29
 
29
30
  describe("postgres integrations", () => {
30
31
  let makeRequest: MakeRequestResponse,
@@ -1055,13 +1056,12 @@ describe("postgres integrations", () => {
1055
1056
  })
1056
1057
  })
1057
1058
 
1058
- describe("GET /api/datasources/:datasourceId/info", () => {
1059
+ describe("POST /api/datasources/info", () => {
1059
1060
  it("should fetch information about postgres datasource", async () => {
1060
1061
  const primaryName = primaryPostgresTable.name
1061
- const response = await makeRequest(
1062
- "get",
1063
- `/api/datasources/${postgresDatasource._id}/info`
1064
- )
1062
+ const response = await makeRequest("post", "/api/datasources/info", {
1063
+ datasource: postgresDatasource,
1064
+ })
1065
1065
  expect(response.status).toBe(200)
1066
1066
  expect(response.body.tableNames).toBeDefined()
1067
1067
  expect(response.body.tableNames.indexOf(primaryName)).not.toBe(-1)
@@ -177,7 +177,13 @@ class RedisIntegration {
177
177
  const pipeline = this.client.pipeline(pipelineCommands)
178
178
  const result = await pipeline.exec()
179
179
 
180
- return result.map((output: string | string[]) => output[1])
180
+ return result?.map((output: any) => {
181
+ if (typeof output === "string") {
182
+ return output
183
+ } else if (Array.isArray(output)) {
184
+ return output[1]
185
+ }
186
+ })
181
187
  })
182
188
  }
183
189
  }
@@ -36,6 +36,6 @@ export async function processUploaded(plugin: FileType, source?: PluginSource) {
36
36
  }
37
37
 
38
38
  const doc = await pro.plugins.storePlugin(metadata, directory, source)
39
- clientAppSocket.emit("plugin-update", { name: doc.name, hash: doc.hash })
39
+ clientAppSocket?.emit("plugin-update", { name: doc.name, hash: doc.hash })
40
40
  return doc
41
41
  }
@@ -44,10 +44,18 @@ function getLoopIterations(loopStep: LoopStep) {
44
44
  if (!binding) {
45
45
  return 0
46
46
  }
47
+ const isString = typeof binding === "string"
48
+ try {
49
+ if (isString) {
50
+ binding = JSON.parse(binding)
51
+ }
52
+ } catch (err) {
53
+ // ignore error - wasn't able to parse
54
+ }
47
55
  if (Array.isArray(binding)) {
48
56
  return binding.length
49
57
  }
50
- if (typeof binding === "string") {
58
+ if (isString) {
51
59
  return automationUtils.stringSplit(binding).length
52
60
  }
53
61
  return 0
@@ -1,13 +1,16 @@
1
- import { redis } from "@budibase/backend-core"
1
+ import { redis, RedisClient } from "@budibase/backend-core"
2
2
  import { getGlobalIDFromUserMetadataID } from "../db/utils"
3
3
  import { ContextUser } from "@budibase/types"
4
+ import env from "../environment"
4
5
 
5
6
  const APP_DEV_LOCK_SECONDS = 600
6
7
  const AUTOMATION_TEST_FLAG_SECONDS = 60
7
- let devAppClient: any, debounceClient: any, flagClient: any
8
+ let devAppClient: RedisClient,
9
+ debounceClient: RedisClient,
10
+ flagClient: RedisClient
8
11
 
9
12
  // We need to maintain a duplicate client for socket.io pub/sub
10
- let socketClient: any
13
+ let socketClient: RedisClient
11
14
  let socketSubClient: any
12
15
 
13
16
  // We init this as we want to keep the connection open all the time
@@ -22,7 +25,9 @@ export async function init() {
22
25
 
23
26
  // Duplicate the socket client for pub/sub
24
27
  socketClient = await redis.clients.getSocketClient()
25
- socketSubClient = socketClient.getClient().duplicate()
28
+ if (!env.isTest()) {
29
+ socketSubClient = socketClient.getClient().duplicate()
30
+ }
26
31
  }
27
32
 
28
33
  export async function shutdown() {
@@ -14,9 +14,9 @@ export default class BuilderSocket extends BaseSocket {
14
14
  super(app, server, "/socket/builder", [authorized(permissions.BUILDER)])
15
15
  }
16
16
 
17
- async onConnect(socket: Socket) {
17
+ async onConnect(socket?: Socket) {
18
18
  // Initial identification of selected app
19
- socket.on(BuilderSocketEvent.SelectApp, async (appId, callback) => {
19
+ socket?.on(BuilderSocketEvent.SelectApp, async (appId, callback) => {
20
20
  await this.joinRoom(socket, appId)
21
21
 
22
22
  // Reply with all users in current room
@@ -49,14 +49,14 @@ export default class BuilderSocket extends BaseSocket {
49
49
  this.io
50
50
  .in(ctx.appId)
51
51
  .emit(BuilderSocketEvent.TableChange, { id: table._id, table })
52
- gridSocket.emitTableUpdate(table)
52
+ gridSocket?.emitTableUpdate(table)
53
53
  }
54
54
 
55
55
  emitTableDeletion(ctx: any, id: string) {
56
56
  this.io
57
57
  .in(ctx.appId)
58
58
  .emit(BuilderSocketEvent.TableChange, { id, table: null })
59
- gridSocket.emitTableDeletion(id)
59
+ gridSocket?.emitTableDeletion(id)
60
60
  }
61
61
 
62
62
  emitDatasourceUpdate(ctx: any, datasource: Datasource) {
@@ -3,15 +3,19 @@ import Koa from "koa"
3
3
  import ClientAppSocket from "./client"
4
4
  import GridSocket from "./grid"
5
5
  import BuilderSocket from "./builder"
6
+ import env from "../environment"
6
7
 
7
- let clientAppSocket: ClientAppSocket
8
- let gridSocket: GridSocket
9
- let builderSocket: BuilderSocket
8
+ let clientAppSocket: ClientAppSocket | undefined
9
+ let gridSocket: GridSocket | undefined
10
+ let builderSocket: BuilderSocket | undefined
10
11
 
11
12
  export const initialise = (app: Koa, server: http.Server) => {
12
- clientAppSocket = new ClientAppSocket(app, server)
13
- gridSocket = new GridSocket(app, server)
14
- builderSocket = new BuilderSocket(app, server)
13
+ // have to remove these for testing until ioredis-mock can be fully removed
14
+ if (!env.isTest()) {
15
+ clientAppSocket = new ClientAppSocket(app, server)
16
+ gridSocket = new GridSocket(app, server)
17
+ builderSocket = new BuilderSocket(app, server)
18
+ }
15
19
  }
16
20
 
17
21
  export { clientAppSocket, gridSocket, builderSocket }