@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/dist/automation.js +33 -15
- package/dist/automation.js.map +3 -3
- package/dist/index.js +82 -48
- package/dist/index.js.map +3 -3
- package/dist/query.js +21 -13
- package/dist/query.js.map +3 -3
- package/package.json +9 -10
- package/src/api/controllers/datasource.ts +24 -18
- package/src/api/controllers/plugin/index.ts +1 -1
- package/src/api/controllers/table/index.ts +2 -2
- package/src/api/controllers/view/index.ts +2 -2
- package/src/api/routes/datasource.ts +2 -2
- package/src/api/routes/tests/view.spec.js +4 -3
- package/src/integration-test/postgres.spec.ts +5 -5
- package/src/integrations/redis.ts +7 -1
- package/src/sdk/plugins/plugins.ts +1 -1
- package/src/threads/automation.ts +9 -1
- package/src/utilities/redis.ts +9 -4
- package/src/websockets/builder.ts +4 -4
- package/src/websockets/index.ts +10 -6
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.
|
|
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.
|
|
50
|
-
"@budibase/client": "2.6.19-alpha.
|
|
51
|
-
"@budibase/pro": "2.6.19-alpha.
|
|
52
|
-
"@budibase/shared-core": "2.6.19-alpha.
|
|
53
|
-
"@budibase/string-templates": "2.6.19-alpha.
|
|
54
|
-
"@budibase/types": "2.6.19-alpha.
|
|
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": "
|
|
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
|
-
|
|
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<
|
|
165
|
+
ctx: UserCtx<FetchDatasourceInfoRequest, FetchDatasourceInfoResponse>
|
|
160
166
|
) {
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const connector = (await getConnector(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
130
|
+
builderSocket?.emitTableUpdate(ctx, table)
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export async function exportView(ctx: Ctx) {
|
|
@@ -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
|
|
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("
|
|
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
|
-
|
|
1063
|
-
|
|
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
|
|
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
|
|
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 (
|
|
58
|
+
if (isString) {
|
|
51
59
|
return automationUtils.stringSplit(binding).length
|
|
52
60
|
}
|
|
53
61
|
return 0
|
package/src/utilities/redis.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
17
|
+
async onConnect(socket?: Socket) {
|
|
18
18
|
// Initial identification of selected app
|
|
19
|
-
socket
|
|
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
|
|
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
|
|
59
|
+
gridSocket?.emitTableDeletion(id)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
emitDatasourceUpdate(ctx: any, datasource: Datasource) {
|
package/src/websockets/index.ts
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 }
|