@budibase/server 1.2.44-alpha.0 → 1.2.46
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/__mocks__/mongodb.ts +0 -4
- package/__mocks__/node-fetch.ts +1 -8
- package/builder/assets/index.08dd0c5e.css +6 -0
- package/builder/assets/index.f6823431.js +975 -0
- package/builder/assets/{vendor.67dbd8fa.js → vendor.6bcafa60.js} +1 -1
- package/builder/index.html +3 -3
- package/coverage/clover.xml +714 -727
- package/coverage/coverage-final.json +27 -27
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +1 -1
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +1 -1
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +1 -1
- package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +35 -35
- package/coverage/lcov-report/api/controllers/row/internal.js.html +88 -46
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +191 -317
- package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +15 -15
- package/coverage/lcov-report/api/controllers/row/utils.js.html +30 -39
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +41 -41
- package/coverage/lcov-report/api/controllers/table/external.js.html +2 -2
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +17 -17
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +19 -19
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +2 -2
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +5 -5
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/utils/index.html +1 -1
- package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +13 -7
- package/coverage/lcov-report/automations/index.html +12 -12
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +1 -1
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +30 -30
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +11 -11
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +19 -19
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +7 -7
- package/coverage/lcov-report/db/newid.js.html +4 -4
- package/coverage/lcov-report/db/utils.js.html +71 -71
- package/coverage/lcov-report/events/AutomationEmitter.js.html +5 -5
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +25 -25
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/builder.js.html +12 -12
- package/coverage/lcov-report/middleware/currentapp.js.html +33 -33
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +31 -31
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +23 -23
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +39 -39
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +10 -10
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +7 -7
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +1168 -1194
- package/dist/api/controllers/row/internal.js +15 -1
- package/dist/api/controllers/row/internalSearch.js +4 -39
- package/dist/api/controllers/row/utils.js +1 -3
- package/dist/api/controllers/static/index.js +2 -8
- package/dist/api/controllers/table/external.js +1 -1
- package/dist/api/routes/static.js +0 -2
- package/dist/automations/bullboard.js +2 -0
- package/dist/automations/listeners.js +102 -0
- package/dist/automations/logging/index.js +0 -3
- package/dist/automations/utils.js +6 -2
- package/dist/integrations/base/sql.js +30 -62
- package/dist/integrations/base/utils.js +27 -13
- package/dist/integrations/microsoftSqlServer.js +2 -5
- package/dist/integrations/mongodb.js +8 -35
- package/dist/integrations/mysql.js +2 -2
- package/dist/integrations/postgres.js +2 -5
- package/dist/package.json +7 -7
- package/dist/threads/automation.js +27 -11
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/index.js +1 -2
- package/package.json +8 -8
- package/src/api/controllers/row/internal.js +15 -1
- package/src/api/controllers/row/internalSearch.js +3 -45
- package/src/api/controllers/row/utils.js +1 -4
- package/src/api/controllers/static/index.ts +1 -5
- package/src/api/controllers/table/external.js +1 -1
- package/src/api/routes/static.ts +0 -11
- package/src/api/routes/tests/row.spec.js +149 -67
- package/src/automations/bullboard.js +2 -0
- package/src/automations/listeners.ts +78 -0
- package/src/automations/logging/index.ts +0 -3
- package/src/automations/utils.ts +7 -6
- package/src/integrations/base/sql.ts +30 -67
- package/src/integrations/base/utils.ts +18 -9
- package/src/integrations/microsoftSqlServer.ts +2 -3
- package/src/integrations/mongodb.ts +8 -35
- package/src/integrations/mysql.ts +1 -1
- package/src/integrations/postgres.ts +1 -2
- package/src/integrations/tests/mongo.spec.js +0 -218
- package/src/integrations/tests/sql.spec.js +11 -101
- package/src/threads/automation.ts +35 -16
- package/src/utilities/fileSystem/index.js +0 -2
- package/builder/assets/index.6b1c808b.css +0 -6
- package/builder/assets/index.7e1eb5c8.js +0 -977
- package/dist/integrations/base/query.js +0 -27
- package/src/api/routes/tests/internalSearch.spec.js +0 -176
- package/src/integrations/base/query.ts +0 -16
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Queue, Job, JobId } from "bull"
|
|
2
|
+
import { AutomationEvent } from "../definitions/automations"
|
|
3
|
+
import * as automation from "../threads/automation"
|
|
4
|
+
|
|
5
|
+
export const addListeners = (queue: Queue) => {
|
|
6
|
+
logging(queue)
|
|
7
|
+
// handleStalled(queue)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const handleStalled = (queue: Queue) => {
|
|
11
|
+
queue.on("active", async (job: Job) => {
|
|
12
|
+
await automation.removeStalled(job as AutomationEvent)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const logging = (queue: Queue) => {
|
|
17
|
+
if (process.env.NODE_DEBUG?.includes("bull")) {
|
|
18
|
+
queue
|
|
19
|
+
.on("error", (error: any) => {
|
|
20
|
+
// An error occurred.
|
|
21
|
+
console.error(`automation-event=error error=${JSON.stringify(error)}`)
|
|
22
|
+
})
|
|
23
|
+
.on("waiting", (jobId: JobId) => {
|
|
24
|
+
// A Job is waiting to be processed as soon as a worker is idling.
|
|
25
|
+
console.log(`automation-event=waiting jobId=${jobId}`)
|
|
26
|
+
})
|
|
27
|
+
.on("active", (job: Job, jobPromise: any) => {
|
|
28
|
+
// A job has started. You can use `jobPromise.cancel()`` to abort it.
|
|
29
|
+
console.log(`automation-event=active jobId=${job.id}`)
|
|
30
|
+
})
|
|
31
|
+
.on("stalled", (job: Job) => {
|
|
32
|
+
// A job has been marked as stalled. This is useful for debugging job
|
|
33
|
+
// workers that crash or pause the event loop.
|
|
34
|
+
console.error(
|
|
35
|
+
`automation-event=stalled jobId=${job.id} job=${JSON.stringify(job)}`
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
.on("progress", (job: Job, progress: any) => {
|
|
39
|
+
// A job's progress was updated!
|
|
40
|
+
console.log(
|
|
41
|
+
`automation-event=progress jobId=${job.id} progress=${progress}`
|
|
42
|
+
)
|
|
43
|
+
})
|
|
44
|
+
.on("completed", (job: Job, result) => {
|
|
45
|
+
// A job successfully completed with a `result`.
|
|
46
|
+
console.log(
|
|
47
|
+
`automation-event=completed jobId=${job.id} result=${result}`
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
.on("failed", (job, err: any) => {
|
|
51
|
+
// A job failed with reason `err`!
|
|
52
|
+
console.log(`automation-event=failed jobId=${job.id} error=${err}`)
|
|
53
|
+
})
|
|
54
|
+
.on("paused", () => {
|
|
55
|
+
// The queue has been paused.
|
|
56
|
+
console.log(`automation-event=paused`)
|
|
57
|
+
})
|
|
58
|
+
.on("resumed", (job: Job) => {
|
|
59
|
+
// The queue has been resumed.
|
|
60
|
+
console.log(`automation-event=paused jobId=${job.id}`)
|
|
61
|
+
})
|
|
62
|
+
.on("cleaned", (jobs: Job[], type: string) => {
|
|
63
|
+
// Old jobs have been cleaned from the queue. `jobs` is an array of cleaned
|
|
64
|
+
// jobs, and `type` is the type of jobs cleaned.
|
|
65
|
+
console.log(
|
|
66
|
+
`automation-event=cleaned length=${jobs.length} type=${type}`
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
.on("drained", () => {
|
|
70
|
+
// Emitted every time the queue has processed all the waiting jobs (even if there can be some delayed jobs not yet processed)
|
|
71
|
+
console.log(`automation-event=drained`)
|
|
72
|
+
})
|
|
73
|
+
.on("removed", (job: Job) => {
|
|
74
|
+
// A job successfully removed.
|
|
75
|
+
console.log(`automation-event=removed jobId=${job.id}`)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -23,9 +23,6 @@ export async function checkAppMetadata(apps: App[]) {
|
|
|
23
23
|
for (let [key, errors] of Object.entries(metadata.automationErrors)) {
|
|
24
24
|
const updated = []
|
|
25
25
|
for (let error of errors) {
|
|
26
|
-
if (!error) {
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
29
26
|
const startDate = error.split(dbUtils.SEPARATOR)[2]
|
|
30
27
|
if (startDate > maxStartDate) {
|
|
31
28
|
updated.push(error)
|
package/src/automations/utils.ts
CHANGED
|
@@ -21,11 +21,13 @@ const WH_STEP_ID = definitions.WEBHOOK.stepId
|
|
|
21
21
|
const CRON_STEP_ID = definitions.CRON.stepId
|
|
22
22
|
const Runner = new Thread(ThreadType.AUTOMATION)
|
|
23
23
|
|
|
24
|
+
const jobMessage = (job: any, message: string) => {
|
|
25
|
+
return `app=${job.data.event.appId} automation=${job.data.automation._id} jobId=${job.id} trigger=${job.data.automation.definition.trigger.event} : ${message}`
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
export async function processEvent(job: any) {
|
|
25
29
|
try {
|
|
26
|
-
console.log(
|
|
27
|
-
`${job.data.automation.appId} automation ${job.data.automation._id} running`
|
|
28
|
-
)
|
|
30
|
+
console.log(jobMessage(job, "running"))
|
|
29
31
|
// need to actually await these so that an error can be captured properly
|
|
30
32
|
const tenantId = tenancy.getTenantIDFromAppID(job.data.event.appId)
|
|
31
33
|
return await tenancy.doInTenant(tenantId, async () => {
|
|
@@ -34,9 +36,7 @@ export async function processEvent(job: any) {
|
|
|
34
36
|
})
|
|
35
37
|
} catch (err) {
|
|
36
38
|
const errJson = JSON.stringify(err)
|
|
37
|
-
console.error(
|
|
38
|
-
`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${errJson}`
|
|
39
|
-
)
|
|
39
|
+
console.error(jobMessage(job, `was unable to run - ${errJson}`))
|
|
40
40
|
console.trace(err)
|
|
41
41
|
return { err }
|
|
42
42
|
}
|
|
@@ -91,6 +91,7 @@ export async function disableAllCrons(appId: any) {
|
|
|
91
91
|
export async function disableCron(jobId: string, jobKey: string) {
|
|
92
92
|
await queue.removeRepeatableByKey(jobKey)
|
|
93
93
|
await queue.removeJobs(jobId)
|
|
94
|
+
console.log(`jobId=${jobId} disabled`)
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
export async function clearMetadata() {
|
|
@@ -10,7 +10,6 @@ import { QueryOptions } from "../../definitions/datasource"
|
|
|
10
10
|
import { isIsoDateString, SqlClient } from "../utils"
|
|
11
11
|
import SqlTableQueryBuilder from "./sqlTable"
|
|
12
12
|
import environment from "../../environment"
|
|
13
|
-
import { removeKeyNumbering } from "./utils"
|
|
14
13
|
|
|
15
14
|
const envLimit = environment.SQL_MAX_ROWS
|
|
16
15
|
? parseInt(environment.SQL_MAX_ROWS)
|
|
@@ -134,13 +133,12 @@ class InternalBuilder {
|
|
|
134
133
|
fn: (key: string, value: any) => void
|
|
135
134
|
) {
|
|
136
135
|
for (let [key, value] of Object.entries(structure)) {
|
|
137
|
-
const
|
|
138
|
-
const isRelationshipField = updatedKey.includes(".")
|
|
136
|
+
const isRelationshipField = key.includes(".")
|
|
139
137
|
if (!opts.relationship && !isRelationshipField) {
|
|
140
|
-
fn(`${opts.tableName}.${
|
|
138
|
+
fn(`${opts.tableName}.${key}`, value)
|
|
141
139
|
}
|
|
142
140
|
if (opts.relationship && isRelationshipField) {
|
|
143
|
-
fn(
|
|
141
|
+
fn(key, value)
|
|
144
142
|
}
|
|
145
143
|
}
|
|
146
144
|
}
|
|
@@ -159,61 +157,6 @@ class InternalBuilder {
|
|
|
159
157
|
}
|
|
160
158
|
}
|
|
161
159
|
|
|
162
|
-
const contains = (mode: object, any: boolean = false) => {
|
|
163
|
-
const fnc = allOr ? "orWhere" : "where"
|
|
164
|
-
const rawFnc = `${fnc}Raw`
|
|
165
|
-
const not = mode === filters?.notContains ? "NOT " : ""
|
|
166
|
-
function stringifyArray(value: Array<any>, quoteStyle = '"'): string {
|
|
167
|
-
for (let i in value) {
|
|
168
|
-
if (typeof value[i] === "string") {
|
|
169
|
-
value[i] = `${quoteStyle}${value[i]}${quoteStyle}`
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return `[${value.join(",")}]`
|
|
173
|
-
}
|
|
174
|
-
if (this.client === SqlClient.POSTGRES) {
|
|
175
|
-
iterate(mode, (key: string, value: Array<any>) => {
|
|
176
|
-
const wrap = any ? "" : "'"
|
|
177
|
-
const containsOp = any ? "\\?| array" : "@>"
|
|
178
|
-
const fieldNames = key.split(/\./g)
|
|
179
|
-
const tableName = fieldNames[0]
|
|
180
|
-
const columnName = fieldNames[1]
|
|
181
|
-
// @ts-ignore
|
|
182
|
-
query = query[rawFnc](
|
|
183
|
-
`${not}"${tableName}"."${columnName}"::jsonb ${containsOp} ${wrap}${stringifyArray(
|
|
184
|
-
value,
|
|
185
|
-
any ? "'" : '"'
|
|
186
|
-
)}${wrap}`
|
|
187
|
-
)
|
|
188
|
-
})
|
|
189
|
-
} else if (this.client === SqlClient.MY_SQL) {
|
|
190
|
-
const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS"
|
|
191
|
-
iterate(mode, (key: string, value: Array<any>) => {
|
|
192
|
-
// @ts-ignore
|
|
193
|
-
query = query[rawFnc](
|
|
194
|
-
`${not}${jsonFnc}(${key}, '${stringifyArray(value)}')`
|
|
195
|
-
)
|
|
196
|
-
})
|
|
197
|
-
} else {
|
|
198
|
-
const andOr = mode === filters?.containsAny ? " OR " : " AND "
|
|
199
|
-
iterate(mode, (key: string, value: Array<any>) => {
|
|
200
|
-
let statement = ""
|
|
201
|
-
for (let i in value) {
|
|
202
|
-
if (typeof value[i] === "string") {
|
|
203
|
-
value[i] = `%"${value[i]}"%`
|
|
204
|
-
} else {
|
|
205
|
-
value[i] = `%${value[i]}%`
|
|
206
|
-
}
|
|
207
|
-
statement +=
|
|
208
|
-
(statement ? andOr : "") +
|
|
209
|
-
`LOWER(${likeKey(this.client, key)}) LIKE ?`
|
|
210
|
-
}
|
|
211
|
-
// @ts-ignore
|
|
212
|
-
query = query[rawFnc](`${not}(${statement})`, value)
|
|
213
|
-
})
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
160
|
if (!filters) {
|
|
218
161
|
return query
|
|
219
162
|
}
|
|
@@ -284,13 +227,32 @@ class InternalBuilder {
|
|
|
284
227
|
})
|
|
285
228
|
}
|
|
286
229
|
if (filters.contains) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
230
|
+
const fnc = allOr ? "orWhere" : "where"
|
|
231
|
+
const rawFnc = `${fnc}Raw`
|
|
232
|
+
if (this.client === SqlClient.POSTGRES) {
|
|
233
|
+
iterate(filters.contains, (key: string, value: any) => {
|
|
234
|
+
const fieldNames = key.split(/\./g)
|
|
235
|
+
const tableName = fieldNames[0]
|
|
236
|
+
const columnName = fieldNames[1]
|
|
237
|
+
if (typeof value === "string") {
|
|
238
|
+
value = `"${value}"`
|
|
239
|
+
}
|
|
240
|
+
// @ts-ignore
|
|
241
|
+
query = query[rawFnc](
|
|
242
|
+
`"${tableName}"."${columnName}"::jsonb @> '[${value}]'`
|
|
243
|
+
)
|
|
244
|
+
})
|
|
245
|
+
} else if (this.client === SqlClient.MY_SQL) {
|
|
246
|
+
iterate(filters.contains, (key: string, value: any) => {
|
|
247
|
+
if (typeof value === "string") {
|
|
248
|
+
value = `"${value}"`
|
|
249
|
+
}
|
|
250
|
+
// @ts-ignore
|
|
251
|
+
query = query[rawFnc](`JSON_CONTAINS(${key}, '${value}')`)
|
|
252
|
+
})
|
|
253
|
+
} else {
|
|
254
|
+
iterate(filters.contains, like)
|
|
255
|
+
}
|
|
294
256
|
}
|
|
295
257
|
return query
|
|
296
258
|
}
|
|
@@ -620,3 +582,4 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
|
|
620
582
|
}
|
|
621
583
|
|
|
622
584
|
export default SqlQueryBuilder
|
|
585
|
+
module.exports = SqlQueryBuilder
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import { QueryJson, Datasource } from "@budibase/types"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
module DatasourceUtils {
|
|
4
|
+
const { integrations } = require("../index")
|
|
5
|
+
|
|
6
|
+
export async function makeExternalQuery(
|
|
7
|
+
datasource: Datasource,
|
|
8
|
+
json: QueryJson
|
|
9
|
+
) {
|
|
10
|
+
const Integration = integrations[datasource.source]
|
|
11
|
+
// query is the opinionated function
|
|
12
|
+
if (Integration.prototype.query) {
|
|
13
|
+
const integration = new Integration(datasource.config)
|
|
14
|
+
return integration.query(json)
|
|
15
|
+
} else {
|
|
16
|
+
throw "Datasource does not support query."
|
|
17
|
+
}
|
|
11
18
|
}
|
|
19
|
+
|
|
20
|
+
module.exports.makeExternalQuery = makeExternalQuery
|
|
12
21
|
}
|
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
finaliseExternalTables,
|
|
17
17
|
SqlClient,
|
|
18
18
|
} from "./utils"
|
|
19
|
-
import Sql from "./base/sql"
|
|
20
19
|
|
|
21
20
|
module MSSQLModule {
|
|
22
21
|
const sqlServer = require("mssql")
|
|
22
|
+
const Sql = require("./base/sql")
|
|
23
23
|
const DEFAULT_SCHEMA = "dbo"
|
|
24
24
|
|
|
25
25
|
interface MSSQLConfig {
|
|
@@ -97,8 +97,7 @@ module MSSQLModule {
|
|
|
97
97
|
class SqlServerIntegration extends Sql implements DatasourcePlus {
|
|
98
98
|
private readonly config: MSSQLConfig
|
|
99
99
|
private index: number = 0
|
|
100
|
-
|
|
101
|
-
private client: any
|
|
100
|
+
static pool: any
|
|
102
101
|
public tables: Record<string, Table> = {}
|
|
103
102
|
public schemaErrors: Record<string, string> = {}
|
|
104
103
|
|
|
@@ -92,15 +92,12 @@ module MongoDBModule {
|
|
|
92
92
|
if (json[field] instanceof Object) {
|
|
93
93
|
json[field] = self.createObjectIds(json[field])
|
|
94
94
|
}
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
typeof json[field] === "string"
|
|
98
|
-
) {
|
|
99
|
-
const id = json[field].match(
|
|
95
|
+
if (field === "_id" && typeof json[field] === "string") {
|
|
96
|
+
const id = json["_id"].match(
|
|
100
97
|
/(?<=objectid\(['"]).*(?=['"]\))/gi
|
|
101
98
|
)?.[0]
|
|
102
99
|
if (id) {
|
|
103
|
-
json[
|
|
100
|
+
json["_id"] = ObjectID.createFromHexString(id)
|
|
104
101
|
}
|
|
105
102
|
}
|
|
106
103
|
}
|
|
@@ -117,31 +114,10 @@ module MongoDBModule {
|
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
parseQueryParams(params: string, mode: string) {
|
|
120
|
-
let queryParams = []
|
|
121
|
-
let
|
|
122
|
-
let
|
|
123
|
-
let
|
|
124
|
-
let startIndex = 0
|
|
125
|
-
for (let c of params) {
|
|
126
|
-
if (c === '"' && i > 0 && params[i - 1] !== "\\") {
|
|
127
|
-
inQuotes = !inQuotes
|
|
128
|
-
}
|
|
129
|
-
if (c === "{" && !inQuotes) {
|
|
130
|
-
openCount++
|
|
131
|
-
if (openCount === 1) {
|
|
132
|
-
startIndex = i
|
|
133
|
-
}
|
|
134
|
-
} else if (c === "}" && !inQuotes) {
|
|
135
|
-
if (openCount === 1) {
|
|
136
|
-
queryParams.push(JSON.parse(params.substring(startIndex, i + 1)))
|
|
137
|
-
}
|
|
138
|
-
openCount--
|
|
139
|
-
}
|
|
140
|
-
i++
|
|
141
|
-
}
|
|
142
|
-
let group1 = queryParams[0] ?? {}
|
|
143
|
-
let group2 = queryParams[1] ?? {}
|
|
144
|
-
let group3 = queryParams[2] ?? {}
|
|
117
|
+
let queryParams = params.split(/(?<=}),[\n\s]*(?={)/g)
|
|
118
|
+
let group1 = queryParams[0] ? JSON.parse(queryParams[0]) : {}
|
|
119
|
+
let group2 = queryParams[1] ? JSON.parse(queryParams[1]) : {}
|
|
120
|
+
let group3 = queryParams[2] ? JSON.parse(queryParams[2]) : {}
|
|
145
121
|
if (mode === "update") {
|
|
146
122
|
return {
|
|
147
123
|
filter: group1,
|
|
@@ -200,10 +176,7 @@ module MongoDBModule {
|
|
|
200
176
|
return await collection.findOne(json)
|
|
201
177
|
}
|
|
202
178
|
case "findOneAndUpdate": {
|
|
203
|
-
|
|
204
|
-
json = this.parseQueryParams(query.json, "update")
|
|
205
|
-
}
|
|
206
|
-
let findAndUpdateJson = this.createObjectIds(json) as {
|
|
179
|
+
let findAndUpdateJson = json as {
|
|
207
180
|
filter: FilterQuery<any>
|
|
208
181
|
update: UpdateQuery<any>
|
|
209
182
|
options: FindOneAndUpdateOption<any>
|
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
} from "./utils"
|
|
18
18
|
import dayjs from "dayjs"
|
|
19
19
|
const { NUMBER_REGEX } = require("../utilities")
|
|
20
|
-
import Sql from "./base/sql"
|
|
21
20
|
|
|
22
21
|
module MySQLModule {
|
|
23
22
|
const mysql = require("mysql2/promise")
|
|
23
|
+
const Sql = require("./base/sql")
|
|
24
24
|
|
|
25
25
|
interface MySQLConfig {
|
|
26
26
|
host: string
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
finaliseExternalTables,
|
|
15
15
|
SqlClient,
|
|
16
16
|
} from "./utils"
|
|
17
|
-
import Sql from "./base/sql"
|
|
18
17
|
|
|
19
18
|
module PostgresModule {
|
|
20
19
|
const { Client, types } = require("pg")
|
|
20
|
+
const Sql = require("./base/sql")
|
|
21
21
|
const { escapeDangerousCharacters } = require("../utilities")
|
|
22
22
|
|
|
23
23
|
// Return "date" and "timestamp" types as plain strings.
|
|
@@ -117,7 +117,6 @@ module PostgresModule {
|
|
|
117
117
|
private readonly client: any
|
|
118
118
|
private readonly config: PostgresConfig
|
|
119
119
|
private index: number = 1
|
|
120
|
-
private open: boolean
|
|
121
120
|
public tables: Record<string, Table> = {}
|
|
122
121
|
public schemaErrors: Record<string, string> = {}
|
|
123
122
|
|
|
@@ -102,222 +102,4 @@ describe("MongoDB Integration", () => {
|
|
|
102
102
|
expect(error).toBeDefined()
|
|
103
103
|
restore()
|
|
104
104
|
})
|
|
105
|
-
|
|
106
|
-
it("creates ObjectIds if the _id fields contains a match on ObjectId", async () => {
|
|
107
|
-
const query = {
|
|
108
|
-
json: {
|
|
109
|
-
filter: {
|
|
110
|
-
_id: "ObjectId('ACBD12345678ABCD12345678')",
|
|
111
|
-
name: "ObjectId('name')"
|
|
112
|
-
},
|
|
113
|
-
update: {
|
|
114
|
-
_id: "ObjectId('FFFF12345678ABCD12345678')",
|
|
115
|
-
name: "ObjectId('updatedName')",
|
|
116
|
-
},
|
|
117
|
-
options: {
|
|
118
|
-
upsert: false,
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
extra: { collection: "testCollection", actionTypes: "updateOne" },
|
|
122
|
-
}
|
|
123
|
-
await config.integration.update(query)
|
|
124
|
-
expect(config.integration.client.updateOne).toHaveBeenCalled()
|
|
125
|
-
|
|
126
|
-
const args = config.integration.client.updateOne.mock.calls[0]
|
|
127
|
-
expect(args[0]).toEqual({
|
|
128
|
-
_id: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"),
|
|
129
|
-
name: "ObjectId('name')",
|
|
130
|
-
})
|
|
131
|
-
expect(args[1]).toEqual({
|
|
132
|
-
_id: mongo.ObjectID.createFromHexString("FFFF12345678ABCD12345678"),
|
|
133
|
-
name: "ObjectId('updatedName')",
|
|
134
|
-
})
|
|
135
|
-
expect(args[2]).toEqual({
|
|
136
|
-
upsert: false
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it("creates ObjectIds if the $ operator fields contains a match on ObjectId", async () => {
|
|
141
|
-
const query = {
|
|
142
|
-
json: {
|
|
143
|
-
filter: {
|
|
144
|
-
_id: {
|
|
145
|
-
$eq: "ObjectId('ACBD12345678ABCD12345678')",
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
update: {
|
|
149
|
-
$set: {
|
|
150
|
-
_id: "ObjectId('FFFF12345678ABCD12345678')",
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
options: {
|
|
154
|
-
upsert: true,
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
extra: { collection: "testCollection", actionTypes: "updateOne" },
|
|
158
|
-
}
|
|
159
|
-
await config.integration.update(query)
|
|
160
|
-
expect(config.integration.client.updateOne).toHaveBeenCalled()
|
|
161
|
-
|
|
162
|
-
const args = config.integration.client.updateOne.mock.calls[0]
|
|
163
|
-
expect(args[0]).toEqual({
|
|
164
|
-
_id: {
|
|
165
|
-
$eq: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"),
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
expect(args[1]).toEqual({
|
|
169
|
-
$set: {
|
|
170
|
-
_id: mongo.ObjectID.createFromHexString("FFFF12345678ABCD12345678"),
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
expect(args[2]).toEqual({
|
|
174
|
-
upsert: true
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it("supports findOneAndUpdate", async () => {
|
|
179
|
-
const query = {
|
|
180
|
-
json: {
|
|
181
|
-
filter: {
|
|
182
|
-
_id: {
|
|
183
|
-
$eq: "ObjectId('ACBD12345678ABCD12345678')",
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
update: {
|
|
187
|
-
$set: {
|
|
188
|
-
name: "UPDATED",
|
|
189
|
-
age: 99
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
options: {
|
|
193
|
-
upsert: false,
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
extra: { collection: "testCollection", actionTypes: "findOneAndUpdate" },
|
|
197
|
-
}
|
|
198
|
-
await config.integration.read(query)
|
|
199
|
-
expect(config.integration.client.findOneAndUpdate).toHaveBeenCalled()
|
|
200
|
-
|
|
201
|
-
const args = config.integration.client.findOneAndUpdate.mock.calls[0]
|
|
202
|
-
expect(args[0]).toEqual({
|
|
203
|
-
_id: {
|
|
204
|
-
$eq: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"),
|
|
205
|
-
}
|
|
206
|
-
})
|
|
207
|
-
expect(args[1]).toEqual({
|
|
208
|
-
$set: {
|
|
209
|
-
name: "UPDATED",
|
|
210
|
-
age: 99
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
expect(args[2]).toEqual({
|
|
214
|
-
upsert: false
|
|
215
|
-
})
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
it("can parse nested objects with arrays", async () => {
|
|
219
|
-
const query = {
|
|
220
|
-
json: `{
|
|
221
|
-
"_id": {
|
|
222
|
-
"$eq": "ObjectId('ACBD12345678ABCD12345678')"
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"$set": {
|
|
227
|
-
"value": {
|
|
228
|
-
"data": [
|
|
229
|
-
{ "cid": 1 },
|
|
230
|
-
{ "cid": 2 },
|
|
231
|
-
{ "nested": {
|
|
232
|
-
"name": "test"
|
|
233
|
-
}}
|
|
234
|
-
]
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
"upsert": true
|
|
240
|
-
}`,
|
|
241
|
-
extra: { collection: "testCollection", actionTypes: "updateOne" },
|
|
242
|
-
}
|
|
243
|
-
await config.integration.update(query)
|
|
244
|
-
expect(config.integration.client.updateOne).toHaveBeenCalled()
|
|
245
|
-
|
|
246
|
-
const args = config.integration.client.updateOne.mock.calls[0]
|
|
247
|
-
expect(args[0]).toEqual({
|
|
248
|
-
_id: {
|
|
249
|
-
$eq: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"),
|
|
250
|
-
}
|
|
251
|
-
})
|
|
252
|
-
expect(args[1]).toEqual({
|
|
253
|
-
$set: {
|
|
254
|
-
value: {
|
|
255
|
-
data: [
|
|
256
|
-
{ cid: 1 },
|
|
257
|
-
{ cid: 2 },
|
|
258
|
-
{ nested: {
|
|
259
|
-
name: "test"
|
|
260
|
-
}}
|
|
261
|
-
]
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
})
|
|
265
|
-
expect(args[2]).toEqual({
|
|
266
|
-
upsert: true
|
|
267
|
-
})
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
it("ignores braces within strings when parsing nested objects", async () => {
|
|
271
|
-
const query = {
|
|
272
|
-
json: `{
|
|
273
|
-
"_id": {
|
|
274
|
-
"$eq": "ObjectId('ACBD12345678ABCD12345678')"
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
{
|
|
278
|
-
"$set": {
|
|
279
|
-
"value": {
|
|
280
|
-
"data": [
|
|
281
|
-
{ "cid": 1 },
|
|
282
|
-
{ "cid": 2 },
|
|
283
|
-
{ "nested": {
|
|
284
|
-
"name": "te}st"
|
|
285
|
-
}}
|
|
286
|
-
]
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
"upsert": true,
|
|
292
|
-
"extra": "ad\\"{\\"d"
|
|
293
|
-
}`,
|
|
294
|
-
extra: { collection: "testCollection", actionTypes: "updateOne" },
|
|
295
|
-
}
|
|
296
|
-
await config.integration.update(query)
|
|
297
|
-
expect(config.integration.client.updateOne).toHaveBeenCalled()
|
|
298
|
-
|
|
299
|
-
const args = config.integration.client.updateOne.mock.calls[0]
|
|
300
|
-
expect(args[0]).toEqual({
|
|
301
|
-
_id: {
|
|
302
|
-
$eq: mongo.ObjectID.createFromHexString("ACBD12345678ABCD12345678"),
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
expect(args[1]).toEqual({
|
|
306
|
-
$set: {
|
|
307
|
-
value: {
|
|
308
|
-
data: [
|
|
309
|
-
{ cid: 1 },
|
|
310
|
-
{ cid: 2 },
|
|
311
|
-
{ nested: {
|
|
312
|
-
name: "te}st"
|
|
313
|
-
}}
|
|
314
|
-
]
|
|
315
|
-
},
|
|
316
|
-
},
|
|
317
|
-
})
|
|
318
|
-
expect(args[2]).toEqual({
|
|
319
|
-
upsert: true,
|
|
320
|
-
extra: "ad\"{\"d"
|
|
321
|
-
})
|
|
322
|
-
})
|
|
323
105
|
})
|