@budibase/server 2.7.21-alpha.3 → 2.7.23
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/builder/assets/{index.91f1d954.js → index.1066b334.js} +347 -347
- package/builder/assets/{index.36462e95.css → index.d9b46807.css} +1 -1
- package/builder/index.html +2 -2
- package/dist/automation.js +180 -421
- package/dist/automation.js.map +3 -3
- package/dist/index.js +283 -532
- package/dist/index.js.map +3 -3
- package/dist/query.js +142 -317
- package/dist/query.js.map +3 -3
- package/package.json +11 -11
- package/src/api/controllers/backup.ts +8 -22
- package/src/api/controllers/datasource.ts +24 -41
- package/src/api/controllers/role.ts +5 -5
- package/src/api/controllers/routing.ts +3 -3
- package/src/api/routes/backup.ts +1 -1
- package/src/api/routes/tests/backup.spec.ts +2 -18
- package/src/automations/steps/sendSmtpEmail.ts +4 -55
- package/src/automations/tests/sendSmtpEmail.spec.js +71 -0
- package/src/events/docUpdates/syncUsers.ts +0 -4
- package/src/integrations/googlesheets.ts +18 -35
- package/src/integrations/mongodb.ts +2 -4
- package/src/integrations/postgres.ts +3 -21
- package/src/middleware/currentapp.ts +1 -1
- package/src/sdk/app/backups/exports.ts +5 -33
- package/src/sdk/app/backups/imports.ts +1 -21
- package/src/sdk/app/datasources/datasources.ts +0 -1
- package/src/utilities/workerRequests.ts +9 -20
- package/src/automations/tests/sendSmtpEmail.spec.ts +0 -74
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.7.
|
|
4
|
+
"version": "2.7.23",
|
|
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.7.
|
|
50
|
-
"@budibase/client": "2.7.
|
|
51
|
-
"@budibase/pro": "2.7.
|
|
52
|
-
"@budibase/shared-core": "2.7.
|
|
53
|
-
"@budibase/string-templates": "2.7.
|
|
54
|
-
"@budibase/types": "2.7.
|
|
49
|
+
"@budibase/backend-core": "2.7.23",
|
|
50
|
+
"@budibase/client": "2.7.23",
|
|
51
|
+
"@budibase/pro": "2.7.23",
|
|
52
|
+
"@budibase/shared-core": "2.7.23",
|
|
53
|
+
"@budibase/string-templates": "2.7.23",
|
|
54
|
+
"@budibase/types": "2.7.23",
|
|
55
55
|
"@bull-board/api": "3.7.0",
|
|
56
56
|
"@bull-board/koa": "3.9.4",
|
|
57
57
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"koa2-ratelimit": "1.1.1",
|
|
98
98
|
"lodash": "4.17.21",
|
|
99
99
|
"memorystream": "0.3.1",
|
|
100
|
-
"mongodb": "
|
|
100
|
+
"mongodb": "4.9",
|
|
101
101
|
"mssql": "6.2.3",
|
|
102
102
|
"mysql2": "2.3.3",
|
|
103
103
|
"node-fetch": "2.6.7",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"socket.io": "4.6.1",
|
|
118
118
|
"svelte": "3.49.0",
|
|
119
119
|
"swagger-parser": "10.0.3",
|
|
120
|
-
"tar": "6.1.
|
|
120
|
+
"tar": "6.1.11",
|
|
121
121
|
"to-json-schema": "0.2.5",
|
|
122
122
|
"uuid": "3.3.2",
|
|
123
123
|
"validate.js": "0.13.1",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"@types/redis": "4.0.11",
|
|
151
151
|
"@types/server-destroy": "1.0.1",
|
|
152
152
|
"@types/supertest": "2.0.12",
|
|
153
|
-
"@types/tar": "6.1.
|
|
153
|
+
"@types/tar": "6.1.3",
|
|
154
154
|
"@typescript-eslint/parser": "5.45.0",
|
|
155
155
|
"apidoc": "0.50.4",
|
|
156
156
|
"babel-jest": "29.5.0",
|
|
@@ -195,5 +195,5 @@
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
},
|
|
198
|
-
"gitHead": "
|
|
198
|
+
"gitHead": "807e9a80d749b8beac7fc57d38499b59036fdeb7"
|
|
199
199
|
}
|
|
@@ -1,31 +1,17 @@
|
|
|
1
1
|
import sdk from "../../sdk"
|
|
2
|
-
import { events, context
|
|
2
|
+
import { events, context } from "@budibase/backend-core"
|
|
3
3
|
import { DocumentType } from "../../db/utils"
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
interface ExportAppDumpRequest {
|
|
7
|
-
excludeRows: boolean
|
|
8
|
-
encryptPassword?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function exportAppDump(ctx: Ctx<ExportAppDumpRequest>) {
|
|
12
|
-
const { appId } = ctx.query as any
|
|
13
|
-
const { excludeRows, encryptPassword } = ctx.request.body
|
|
14
|
-
|
|
15
|
-
const [app] = await db.getAppsByIDs([appId])
|
|
16
|
-
const appName = app.name
|
|
4
|
+
import { isQsTrue } from "../../utilities"
|
|
17
5
|
|
|
6
|
+
export async function exportAppDump(ctx: any) {
|
|
7
|
+
let { appId, excludeRows } = ctx.query
|
|
18
8
|
// remove the 120 second limit for the request
|
|
19
9
|
ctx.req.setTimeout(0)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const backupIdentifier = `${appName}-export-${new Date().getTime()}
|
|
10
|
+
const appName = decodeURI(ctx.query.appname)
|
|
11
|
+
excludeRows = isQsTrue(excludeRows)
|
|
12
|
+
const backupIdentifier = `${appName}-export-${new Date().getTime()}.tar.gz`
|
|
23
13
|
ctx.attachment(backupIdentifier)
|
|
24
|
-
ctx.body = await sdk.backups.streamExportApp(
|
|
25
|
-
appId,
|
|
26
|
-
excludeRows,
|
|
27
|
-
encryptPassword,
|
|
28
|
-
})
|
|
14
|
+
ctx.body = await sdk.backups.streamExportApp(appId, excludeRows)
|
|
29
15
|
|
|
30
16
|
await context.doInAppContext(appId, async () => {
|
|
31
17
|
const appDb = context.getAppDB()
|
|
@@ -11,7 +11,7 @@ import { BuildSchemaErrors, InvalidColumns } from "../../constants"
|
|
|
11
11
|
import { getIntegration } from "../../integrations"
|
|
12
12
|
import { getDatasourceAndQuery } from "./row/utils"
|
|
13
13
|
import { invalidateDynamicVariables } from "../../threads/utils"
|
|
14
|
-
import { db as dbCore, context, events
|
|
14
|
+
import { db as dbCore, context, events } from "@budibase/backend-core"
|
|
15
15
|
import {
|
|
16
16
|
UserCtx,
|
|
17
17
|
Datasource,
|
|
@@ -25,11 +25,9 @@ import {
|
|
|
25
25
|
FetchDatasourceInfoResponse,
|
|
26
26
|
IntegrationBase,
|
|
27
27
|
DatasourcePlus,
|
|
28
|
-
SourceName,
|
|
29
28
|
} from "@budibase/types"
|
|
30
29
|
import sdk from "../../sdk"
|
|
31
30
|
import { builderSocket } from "../../websockets"
|
|
32
|
-
import { setupCreationAuth as googleSetupCreationAuth } from "../../integrations/googlesheets"
|
|
33
31
|
|
|
34
32
|
function getErrorTables(errors: any, errorType: string) {
|
|
35
33
|
return Object.entries(errors)
|
|
@@ -103,22 +101,6 @@ async function buildSchemaHelper(datasource: Datasource) {
|
|
|
103
101
|
return { tables: connector.tables, error }
|
|
104
102
|
}
|
|
105
103
|
|
|
106
|
-
async function buildFilteredSchema(datasource: Datasource, filter?: string[]) {
|
|
107
|
-
let { tables, error } = await buildSchemaHelper(datasource)
|
|
108
|
-
let finalTables = tables
|
|
109
|
-
if (filter) {
|
|
110
|
-
finalTables = {}
|
|
111
|
-
for (let key in tables) {
|
|
112
|
-
if (
|
|
113
|
-
filter.some((filter: any) => filter.toLowerCase() === key.toLowerCase())
|
|
114
|
-
) {
|
|
115
|
-
finalTables[key] = tables[key]
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return { tables: finalTables, error }
|
|
120
|
-
}
|
|
121
|
-
|
|
122
104
|
export async function fetch(ctx: UserCtx) {
|
|
123
105
|
// Get internal tables
|
|
124
106
|
const db = context.getAppDB()
|
|
@@ -190,28 +172,43 @@ export async function information(
|
|
|
190
172
|
}
|
|
191
173
|
const tableNames = await connector.getTableNames()
|
|
192
174
|
ctx.body = {
|
|
193
|
-
tableNames
|
|
175
|
+
tableNames,
|
|
194
176
|
}
|
|
195
177
|
}
|
|
196
178
|
|
|
197
179
|
export async function buildSchemaFromDb(ctx: UserCtx) {
|
|
198
180
|
const db = context.getAppDB()
|
|
199
|
-
const tablesFilter = ctx.request.body.tablesFilter
|
|
200
181
|
const datasource = await sdk.datasources.get(ctx.params.datasourceId)
|
|
182
|
+
const tablesFilter = ctx.request.body.tablesFilter
|
|
201
183
|
|
|
202
|
-
|
|
203
|
-
|
|
184
|
+
let { tables, error } = await buildSchemaHelper(datasource)
|
|
185
|
+
if (tablesFilter) {
|
|
186
|
+
if (!datasource.entities) {
|
|
187
|
+
datasource.entities = {}
|
|
188
|
+
}
|
|
189
|
+
for (let key in tables) {
|
|
190
|
+
if (
|
|
191
|
+
tablesFilter.some(
|
|
192
|
+
(filter: any) => filter.toLowerCase() === key.toLowerCase()
|
|
193
|
+
)
|
|
194
|
+
) {
|
|
195
|
+
datasource.entities[key] = tables[key]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
datasource.entities = tables
|
|
200
|
+
}
|
|
204
201
|
|
|
205
202
|
setDefaultDisplayColumns(datasource)
|
|
206
203
|
const dbResp = await db.put(datasource)
|
|
207
204
|
datasource._rev = dbResp.rev
|
|
208
205
|
const cleanedDatasource = await sdk.datasources.removeSecretSingle(datasource)
|
|
209
206
|
|
|
210
|
-
const
|
|
207
|
+
const response: any = { datasource: cleanedDatasource }
|
|
211
208
|
if (error) {
|
|
212
|
-
|
|
209
|
+
response.error = error
|
|
213
210
|
}
|
|
214
|
-
ctx.body =
|
|
211
|
+
ctx.body = response
|
|
215
212
|
}
|
|
216
213
|
|
|
217
214
|
/**
|
|
@@ -309,19 +306,12 @@ export async function update(ctx: UserCtx<any, UpdateDatasourceResponse>) {
|
|
|
309
306
|
builderSocket?.emitDatasourceUpdate(ctx, datasource)
|
|
310
307
|
}
|
|
311
308
|
|
|
312
|
-
const preSaveAction: Partial<Record<SourceName, any>> = {
|
|
313
|
-
[SourceName.GOOGLE_SHEETS]: async (datasource: Datasource) => {
|
|
314
|
-
await googleSetupCreationAuth(datasource.config as any)
|
|
315
|
-
},
|
|
316
|
-
}
|
|
317
|
-
|
|
318
309
|
export async function save(
|
|
319
310
|
ctx: UserCtx<CreateDatasourceRequest, CreateDatasourceResponse>
|
|
320
311
|
) {
|
|
321
312
|
const db = context.getAppDB()
|
|
322
313
|
const plus = ctx.request.body.datasource.plus
|
|
323
314
|
const fetchSchema = ctx.request.body.fetchSchema
|
|
324
|
-
const tablesFilter = ctx.request.body.tablesFilter
|
|
325
315
|
|
|
326
316
|
const datasource = {
|
|
327
317
|
_id: generateDatasourceID({ plus }),
|
|
@@ -331,19 +321,12 @@ export async function save(
|
|
|
331
321
|
|
|
332
322
|
let schemaError = null
|
|
333
323
|
if (fetchSchema) {
|
|
334
|
-
const { tables, error } = await
|
|
335
|
-
datasource,
|
|
336
|
-
tablesFilter
|
|
337
|
-
)
|
|
324
|
+
const { tables, error } = await buildSchemaHelper(datasource)
|
|
338
325
|
schemaError = error
|
|
339
326
|
datasource.entities = tables
|
|
340
327
|
setDefaultDisplayColumns(datasource)
|
|
341
328
|
}
|
|
342
329
|
|
|
343
|
-
if (preSaveAction[datasource.source]) {
|
|
344
|
-
await preSaveAction[datasource.source](datasource)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
330
|
const dbResp = await db.put(datasource)
|
|
348
331
|
await events.datasource.created(datasource)
|
|
349
332
|
datasource._rev = dbResp.rev
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getUserMetadataParams,
|
|
5
5
|
InternalTables,
|
|
6
6
|
} from "../../db/utils"
|
|
7
|
-
import {
|
|
7
|
+
import { BBContext, Database } from "@budibase/types"
|
|
8
8
|
|
|
9
9
|
const UpdateRolesOptions = {
|
|
10
10
|
CREATED: "created",
|
|
@@ -38,15 +38,15 @@ async function updateRolesOnUserTable(
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export async function fetch(ctx:
|
|
41
|
+
export async function fetch(ctx: BBContext) {
|
|
42
42
|
ctx.body = await roles.getAllRoles()
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export async function find(ctx:
|
|
45
|
+
export async function find(ctx: BBContext) {
|
|
46
46
|
ctx.body = await roles.getRole(ctx.params.roleId)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export async function save(ctx:
|
|
49
|
+
export async function save(ctx: BBContext) {
|
|
50
50
|
const db = context.getAppDB()
|
|
51
51
|
let { _id, name, inherits, permissionId } = ctx.request.body
|
|
52
52
|
let isCreate = false
|
|
@@ -72,7 +72,7 @@ export async function save(ctx: UserCtx) {
|
|
|
72
72
|
ctx.message = `Role '${role.name}' created successfully.`
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export async function destroy(ctx:
|
|
75
|
+
export async function destroy(ctx: BBContext) {
|
|
76
76
|
const db = context.getAppDB()
|
|
77
77
|
const roleId = ctx.params.roleId
|
|
78
78
|
const role = await db.get(roleId)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getRoutingInfo } from "../../utilities/routing"
|
|
2
2
|
import { roles } from "@budibase/backend-core"
|
|
3
|
-
import {
|
|
3
|
+
import { BBContext } from "@budibase/types"
|
|
4
4
|
|
|
5
5
|
const URL_SEPARATOR = "/"
|
|
6
6
|
|
|
@@ -56,11 +56,11 @@ async function getRoutingStructure() {
|
|
|
56
56
|
return { routes: routing.json }
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export async function fetch(ctx:
|
|
59
|
+
export async function fetch(ctx: BBContext) {
|
|
60
60
|
ctx.body = await getRoutingStructure()
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export async function clientFetch(ctx:
|
|
63
|
+
export async function clientFetch(ctx: BBContext) {
|
|
64
64
|
const routing = await getRoutingStructure()
|
|
65
65
|
let roleId = ctx.user?.role?._id
|
|
66
66
|
const roleIds = (await roles.getUserRoleHierarchy(roleId, {
|
package/src/api/routes/backup.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import tk from "timekeeper"
|
|
2
1
|
import * as setup from "./utilities"
|
|
3
2
|
import { events } from "@budibase/backend-core"
|
|
4
3
|
import sdk from "../../../sdk"
|
|
5
4
|
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
|
6
|
-
import { mocks } from "@budibase/backend-core/tests"
|
|
7
5
|
|
|
8
6
|
describe("/backups", () => {
|
|
9
7
|
let request = setup.getRequest()
|
|
@@ -18,7 +16,7 @@ describe("/backups", () => {
|
|
|
18
16
|
describe("exportAppDump", () => {
|
|
19
17
|
it("should be able to export app", async () => {
|
|
20
18
|
const res = await request
|
|
21
|
-
.
|
|
19
|
+
.get(`/api/backups/export?appId=${config.getAppId()}&appname=test`)
|
|
22
20
|
.set(config.defaultHeaders())
|
|
23
21
|
.expect(200)
|
|
24
22
|
expect(res.headers["content-type"]).toEqual("application/gzip")
|
|
@@ -28,24 +26,10 @@ describe("/backups", () => {
|
|
|
28
26
|
it("should apply authorization to endpoint", async () => {
|
|
29
27
|
await checkBuilderEndpoint({
|
|
30
28
|
config,
|
|
31
|
-
method: "
|
|
29
|
+
method: "GET",
|
|
32
30
|
url: `/api/backups/export?appId=${config.getAppId()}`,
|
|
33
31
|
})
|
|
34
32
|
})
|
|
35
|
-
|
|
36
|
-
it("should infer the app name from the app", async () => {
|
|
37
|
-
tk.freeze(mocks.date.MOCK_DATE)
|
|
38
|
-
|
|
39
|
-
const res = await request
|
|
40
|
-
.post(`/api/backups/export?appId=${config.getAppId()}`)
|
|
41
|
-
.set(config.defaultHeaders())
|
|
42
|
-
|
|
43
|
-
expect(res.headers["content-disposition"]).toEqual(
|
|
44
|
-
`attachment; filename="${
|
|
45
|
-
config.getApp()!.name
|
|
46
|
-
}-export-${mocks.date.MOCK_DATE.getTime()}.tar.gz"`
|
|
47
|
-
)
|
|
48
|
-
})
|
|
49
33
|
})
|
|
50
34
|
|
|
51
35
|
describe("calculateBackupStats", () => {
|
|
@@ -48,35 +48,6 @@ export const definition: AutomationStepSchema = {
|
|
|
48
48
|
type: AutomationIOType.STRING,
|
|
49
49
|
title: "HTML Contents",
|
|
50
50
|
},
|
|
51
|
-
addInvite: {
|
|
52
|
-
type: AutomationIOType.BOOLEAN,
|
|
53
|
-
title: "Add calendar invite",
|
|
54
|
-
},
|
|
55
|
-
startTime: {
|
|
56
|
-
type: AutomationIOType.DATE,
|
|
57
|
-
title: "Start Time",
|
|
58
|
-
dependsOn: "addInvite",
|
|
59
|
-
},
|
|
60
|
-
endTime: {
|
|
61
|
-
type: AutomationIOType.DATE,
|
|
62
|
-
title: "End Time",
|
|
63
|
-
dependsOn: "addInvite",
|
|
64
|
-
},
|
|
65
|
-
summary: {
|
|
66
|
-
type: AutomationIOType.STRING,
|
|
67
|
-
title: "Meeting Summary",
|
|
68
|
-
dependsOn: "addInvite",
|
|
69
|
-
},
|
|
70
|
-
location: {
|
|
71
|
-
type: AutomationIOType.STRING,
|
|
72
|
-
title: "Location",
|
|
73
|
-
dependsOn: "addInvite",
|
|
74
|
-
},
|
|
75
|
-
url: {
|
|
76
|
-
type: AutomationIOType.STRING,
|
|
77
|
-
title: "URL",
|
|
78
|
-
dependsOn: "addInvite",
|
|
79
|
-
},
|
|
80
51
|
},
|
|
81
52
|
required: ["to", "from", "subject", "contents"],
|
|
82
53
|
},
|
|
@@ -97,43 +68,21 @@ export const definition: AutomationStepSchema = {
|
|
|
97
68
|
}
|
|
98
69
|
|
|
99
70
|
export async function run({ inputs }: AutomationStepInput) {
|
|
100
|
-
let {
|
|
101
|
-
to,
|
|
102
|
-
from,
|
|
103
|
-
subject,
|
|
104
|
-
contents,
|
|
105
|
-
cc,
|
|
106
|
-
bcc,
|
|
107
|
-
addInvite,
|
|
108
|
-
startTime,
|
|
109
|
-
endTime,
|
|
110
|
-
summary,
|
|
111
|
-
location,
|
|
112
|
-
url,
|
|
113
|
-
} = inputs
|
|
71
|
+
let { to, from, subject, contents, cc, bcc } = inputs
|
|
114
72
|
if (!contents) {
|
|
115
73
|
contents = "<h1>No content</h1>"
|
|
116
74
|
}
|
|
117
75
|
to = to || undefined
|
|
118
76
|
try {
|
|
119
|
-
let response = await sendSmtpEmail(
|
|
77
|
+
let response = await sendSmtpEmail(
|
|
120
78
|
to,
|
|
121
79
|
from,
|
|
122
80
|
subject,
|
|
123
81
|
contents,
|
|
124
82
|
cc,
|
|
125
83
|
bcc,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
? {
|
|
129
|
-
startTime,
|
|
130
|
-
endTime,
|
|
131
|
-
summary,
|
|
132
|
-
location,
|
|
133
|
-
url,
|
|
134
|
-
}
|
|
135
|
-
: undefined,
|
|
136
|
-
})
|
|
84
|
+
true
|
|
85
|
+
)
|
|
137
86
|
return {
|
|
138
87
|
success: true,
|
|
139
88
|
response,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
function generateResponse(to, from) {
|
|
3
|
+
return {
|
|
4
|
+
"success": true,
|
|
5
|
+
"response": {
|
|
6
|
+
"accepted": [
|
|
7
|
+
to
|
|
8
|
+
],
|
|
9
|
+
"envelope": {
|
|
10
|
+
"from": from,
|
|
11
|
+
"to": [
|
|
12
|
+
to
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"message": `Email sent to ${to}.`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const mockFetch = jest.fn(() => ({
|
|
22
|
+
headers: {
|
|
23
|
+
raw: () => {
|
|
24
|
+
return { "content-type": ["application/json"] }
|
|
25
|
+
},
|
|
26
|
+
get: () => ["application/json"],
|
|
27
|
+
},
|
|
28
|
+
json: jest.fn(() => response),
|
|
29
|
+
status: 200,
|
|
30
|
+
text: jest.fn(),
|
|
31
|
+
}))
|
|
32
|
+
jest.mock("node-fetch", () => mockFetch)
|
|
33
|
+
const setup = require("./utilities")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
describe("test the outgoing webhook action", () => {
|
|
37
|
+
let inputs
|
|
38
|
+
let config = setup.getConfig()
|
|
39
|
+
beforeAll(async () => {
|
|
40
|
+
await config.init()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
afterAll(setup.afterAll)
|
|
44
|
+
|
|
45
|
+
it("should be able to run the action", async () => {
|
|
46
|
+
inputs = {
|
|
47
|
+
to: "user1@test.com",
|
|
48
|
+
from: "admin@test.com",
|
|
49
|
+
subject: "hello",
|
|
50
|
+
contents: "testing",
|
|
51
|
+
}
|
|
52
|
+
let resp = generateResponse(inputs.to, inputs.from)
|
|
53
|
+
mockFetch.mockImplementationOnce(() => ({
|
|
54
|
+
headers: {
|
|
55
|
+
raw: () => {
|
|
56
|
+
return { "content-type": ["application/json"] }
|
|
57
|
+
},
|
|
58
|
+
get: () => ["application/json"],
|
|
59
|
+
},
|
|
60
|
+
json: jest.fn(() => resp),
|
|
61
|
+
status: 200,
|
|
62
|
+
text: jest.fn(),
|
|
63
|
+
}))
|
|
64
|
+
const res = await setup.runStep(setup.actions.SEND_EMAIL_SMTP.stepId, inputs)
|
|
65
|
+
expect(res.response).toEqual(resp)
|
|
66
|
+
expect(res.success).toEqual(true)
|
|
67
|
+
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
})
|
|
@@ -26,10 +26,6 @@ export default function process(updateCb?: UpdateCallback) {
|
|
|
26
26
|
// if something not found - no changes to perform
|
|
27
27
|
if (err?.status === 404) {
|
|
28
28
|
return
|
|
29
|
-
}
|
|
30
|
-
// The user has already been sync in another process
|
|
31
|
-
else if (err?.status === 409) {
|
|
32
|
-
return
|
|
33
29
|
} else {
|
|
34
30
|
logging.logAlert("Failed to perform user/group app sync", err)
|
|
35
31
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConnectionInfo,
|
|
3
|
-
Datasource,
|
|
4
3
|
DatasourceFeature,
|
|
5
4
|
DatasourceFieldType,
|
|
6
5
|
DatasourcePlus,
|
|
@@ -20,15 +19,13 @@ import { OAuth2Client } from "google-auth-library"
|
|
|
20
19
|
import { buildExternalTableId, finaliseExternalTables } from "./utils"
|
|
21
20
|
import { GoogleSpreadsheet, GoogleSpreadsheetRow } from "google-spreadsheet"
|
|
22
21
|
import fetch from "node-fetch"
|
|
23
|
-
import {
|
|
24
|
-
import { dataFilters
|
|
22
|
+
import { configs, HTTPError } from "@budibase/backend-core"
|
|
23
|
+
import { dataFilters } from "@budibase/shared-core"
|
|
25
24
|
import { GOOGLE_SHEETS_PRIMARY_KEY } from "../constants"
|
|
26
|
-
import sdk from "../sdk"
|
|
27
25
|
|
|
28
26
|
interface GoogleSheetsConfig {
|
|
29
27
|
spreadsheetId: string
|
|
30
28
|
auth: OAuthClientConfig
|
|
31
|
-
continueSetupId?: string
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
interface OAuthClientConfig {
|
|
@@ -75,7 +72,7 @@ const SCHEMA: Integration = {
|
|
|
75
72
|
},
|
|
76
73
|
datasource: {
|
|
77
74
|
spreadsheetId: {
|
|
78
|
-
display: "
|
|
75
|
+
display: "Google Sheet URL",
|
|
79
76
|
type: DatasourceFieldType.STRING,
|
|
80
77
|
required: true,
|
|
81
78
|
},
|
|
@@ -210,8 +207,6 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
210
207
|
|
|
211
208
|
async connect() {
|
|
212
209
|
try {
|
|
213
|
-
await setupCreationAuth(this.config)
|
|
214
|
-
|
|
215
210
|
// Initialise oAuth client
|
|
216
211
|
let googleConfig = await configs.getGoogleDatasourceConfig()
|
|
217
212
|
if (!googleConfig) {
|
|
@@ -274,24 +269,24 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
274
269
|
}
|
|
275
270
|
|
|
276
271
|
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
|
272
|
+
// not fully configured yet
|
|
273
|
+
if (!this.config.auth) {
|
|
274
|
+
return
|
|
275
|
+
}
|
|
277
276
|
await this.connect()
|
|
278
277
|
const sheets = this.client.sheetsByIndex
|
|
279
278
|
const tables: Record<string, Table> = {}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
)
|
|
292
|
-
},
|
|
293
|
-
10
|
|
294
|
-
)
|
|
279
|
+
for (let sheet of sheets) {
|
|
280
|
+
// must fetch rows to determine schema
|
|
281
|
+
await sheet.getRows()
|
|
282
|
+
|
|
283
|
+
const id = buildExternalTableId(datasourceId, sheet.title)
|
|
284
|
+
tables[sheet.title] = this.getTableSchema(
|
|
285
|
+
sheet.title,
|
|
286
|
+
sheet.headerValues,
|
|
287
|
+
id
|
|
288
|
+
)
|
|
289
|
+
}
|
|
295
290
|
const final = finaliseExternalTables(tables, entities)
|
|
296
291
|
this.tables = final.tables
|
|
297
292
|
this.schemaErrors = final.errors
|
|
@@ -571,18 +566,6 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
571
566
|
}
|
|
572
567
|
}
|
|
573
568
|
|
|
574
|
-
export async function setupCreationAuth(datasouce: GoogleSheetsConfig) {
|
|
575
|
-
if (datasouce.continueSetupId) {
|
|
576
|
-
const appId = context.getAppId()
|
|
577
|
-
const tokens = await cache.get(
|
|
578
|
-
`datasource:creation:${appId}:google:${datasouce.continueSetupId}`
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
datasouce.auth = tokens.tokens
|
|
582
|
-
delete datasouce.continueSetupId
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
569
|
export default {
|
|
587
570
|
schema: SCHEMA,
|
|
588
571
|
integration: GoogleSheetsIntegration,
|
|
@@ -351,7 +351,7 @@ const SCHEMA: Integration = getSchema()
|
|
|
351
351
|
|
|
352
352
|
class MongoIntegration implements IntegrationBase {
|
|
353
353
|
private config: MongoDBConfig
|
|
354
|
-
private client:
|
|
354
|
+
private client: any
|
|
355
355
|
|
|
356
356
|
constructor(config: MongoDBConfig) {
|
|
357
357
|
this.config = config
|
|
@@ -372,8 +372,6 @@ class MongoIntegration implements IntegrationBase {
|
|
|
372
372
|
response.connected = true
|
|
373
373
|
} catch (e: any) {
|
|
374
374
|
response.error = e.message as string
|
|
375
|
-
} finally {
|
|
376
|
-
await this.client.close()
|
|
377
375
|
}
|
|
378
376
|
return response
|
|
379
377
|
}
|
|
@@ -382,7 +380,7 @@ class MongoIntegration implements IntegrationBase {
|
|
|
382
380
|
return this.client.connect()
|
|
383
381
|
}
|
|
384
382
|
|
|
385
|
-
createObjectIds(json: any) {
|
|
383
|
+
createObjectIds(json: any): object {
|
|
386
384
|
const self = this
|
|
387
385
|
function interpolateObjectIds(json: any) {
|
|
388
386
|
for (let field of Object.keys(json)) {
|