@budibase/server 0.9.184 → 0.9.185-alpha.11
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/Dockerfile +1 -1
- package/__mocks__/mssql.ts +1 -1
- package/__mocks__/pg.ts +3 -0
- package/builder/assets/bb-space-man.5be51a45.svg +290 -0
- package/builder/assets/index.053c6f7a.js +1321 -0
- package/builder/assets/index.4a7c5636.css +1 -0
- package/builder/assets/vendor.470042a4.js +152 -0
- package/builder/assets/vendor.5e1fe644.css +1 -0
- package/builder/index.html +4 -4
- package/coverage/clover.xml +1357 -959
- package/coverage/coverage-final.json +91 -87
- package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
- package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
- package/coverage/lcov-report/api/controllers/application.js.html +65 -14
- package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
- package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
- 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 +4 -4
- package/coverage/lcov-report/api/controllers/datasource.js.html +147 -27
- package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
- package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
- package/coverage/lcov-report/api/controllers/index.html +90 -90
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
- package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
- package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
- package/coverage/lcov-report/api/controllers/query.js.html +53 -209
- package/coverage/lcov-report/api/controllers/role.js.html +9 -9
- package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
- package/coverage/lcov-report/api/controllers/row/external.js.html +12 -6
- package/coverage/lcov-report/api/controllers/row/index.html +26 -26
- package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
- package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
- package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
- package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
- package/coverage/lcov-report/api/controllers/script.js.html +3 -3
- package/coverage/lcov-report/api/controllers/table/external.js.html +980 -0
- package/coverage/lcov-report/api/controllers/table/index.html +56 -26
- package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
- package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
- package/coverage/lcov-report/api/controllers/table/utils.js.html +172 -49
- package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
- package/coverage/lcov-report/api/controllers/user.js.html +390 -33
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +9 -9
- package/coverage/lcov-report/api/controllers/view/index.js.html +5 -5
- package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
- package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +1 -1
- 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/application.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 +2 -2
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +4 -4
- package/coverage/lcov-report/api/routes/index.js.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/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +5 -2
- 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/row.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/static.js.html +2 -2
- package/coverage/lcov-report/api/routes/table.js.html +35 -2
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +17 -2
- 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 +2 -2
- package/coverage/lcov-report/automations/automationUtils.js.html +3 -3
- package/coverage/lcov-report/automations/bullboard.js.html +71 -20
- package/coverage/lcov-report/automations/index.html +33 -48
- package/coverage/lcov-report/automations/index.js.html +2 -2
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +14 -14
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
- package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
- 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 +7 -7
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
- package/coverage/lcov-report/automations/triggers.js.html +22 -10
- package/coverage/lcov-report/automations/utils.js.html +42 -12
- package/coverage/lcov-report/constants/index.html +5 -5
- package/coverage/lcov-report/constants/index.js.html +49 -4
- 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/client.js.html +2 -2
- package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
- package/coverage/lcov-report/db/index.html +2 -2
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
- package/coverage/lcov-report/db/linkedRows/index.html +10 -10
- package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
- package/coverage/lcov-report/db/newid.js.html +1 -1
- package/coverage/lcov-report/db/utils.js.html +1 -1
- package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
- package/coverage/lcov-report/events/index.html +6 -6
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +5 -5
- package/coverage/lcov-report/index.html +98 -83
- 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 +2 -2
- package/coverage/lcov-report/middleware/authorized.js.html +11 -8
- package/coverage/lcov-report/middleware/builder.js.html +16 -7
- package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
- package/coverage/lcov-report/middleware/index.html +23 -23
- package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
- package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
- package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
- package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
- package/coverage/lcov-report/middleware/utils.js.html +1 -1
- package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +83 -32
- package/coverage/lcov-report/threads/index.html +141 -0
- package/coverage/lcov-report/threads/index.js.html +260 -0
- package/coverage/lcov-report/threads/query.js.html +269 -0
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- 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 +144 -15
- package/coverage/lcov-report/utilities/global.js.html +26 -11
- package/coverage/lcov-report/utilities/index.html +49 -49
- package/coverage/lcov-report/utilities/index.js.html +65 -17
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
- package/coverage/lcov-report/utilities/queue/index.html +14 -14
- 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 +4 -4
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
- package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
- package/coverage/lcov-report/utilities/security.js.html +3 -3
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
- package/coverage/lcov.info +2301 -1599
- package/dist/api/controllers/application.js +16 -2
- package/dist/api/controllers/auth.js +3 -3
- package/dist/api/controllers/datasource.js +47 -16
- package/dist/api/controllers/deploy/index.js +2 -0
- package/dist/api/controllers/dev.js +4 -1
- package/dist/api/controllers/query.js +30 -58
- package/dist/api/controllers/row/ExternalRequest.js +37 -14
- package/dist/api/controllers/row/external.js +1 -0
- package/dist/api/controllers/row/utils.js +6 -12
- package/dist/api/controllers/static/index.js +1 -1
- package/dist/api/controllers/table/external.js +242 -0
- package/dist/api/controllers/table/index.js +49 -123
- package/dist/api/controllers/table/internal.js +150 -0
- package/dist/api/controllers/table/utils.js +62 -28
- package/dist/api/controllers/user.js +123 -11
- package/dist/api/routes/query.js +1 -1
- package/dist/api/routes/table.js +7 -1
- package/dist/api/routes/user.js +2 -1
- package/dist/app.js +2 -11
- package/dist/automations/bullboard.js +37 -10
- package/dist/automations/triggers.js +7 -3
- package/dist/automations/utils.js +11 -2
- package/dist/constants/index.js +13 -0
- package/dist/definitions/datasource.js +4 -0
- package/dist/integrations/base/IntegrationBase.js +2 -0
- package/dist/integrations/base/datasourcePlus.js +2 -0
- package/dist/integrations/base/sql.js +334 -180
- package/dist/integrations/base/sqlTable.js +143 -0
- package/dist/integrations/base/utils.js +30 -0
- package/dist/integrations/microsoftSqlServer.js +117 -9
- package/dist/integrations/mysql.js +14 -84
- package/dist/integrations/postgres.js +35 -28
- package/dist/integrations/utils.js +56 -9
- package/dist/middleware/authorized.js +2 -1
- package/dist/middleware/builder.js +4 -1
- package/dist/package.json +7 -6
- package/dist/tests/utilities/TestConfiguration.js +2 -1
- package/dist/{automations/thread.js → threads/automation.js} +23 -7
- package/dist/threads/index.js +58 -0
- package/dist/threads/query.js +61 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/csvParser.js +46 -5
- package/dist/utilities/global.js +6 -2
- package/dist/utilities/index.js +18 -2
- package/dist/utilities/queue/inMemoryQueue.js +8 -0
- package/dist/utilities/scriptRunner.js +4 -1
- package/package.json +8 -7
- package/scripts/integrations/mssql/data/Dockerfile +9 -0
- package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
- package/scripts/integrations/mssql/data/setup.sql +54 -0
- package/scripts/integrations/mssql/docker-compose.yaml +12 -0
- package/scripts/integrations/mssql/reset.sh +3 -0
- package/scripts/likeCypress.ts +33 -0
- package/src/api/controllers/application.js +19 -2
- package/src/api/controllers/auth.js +3 -0
- package/src/api/controllers/datasource.js +56 -16
- package/src/api/controllers/deploy/index.js +2 -0
- package/src/api/controllers/dev.js +4 -1
- package/src/api/controllers/query.js +29 -81
- package/src/api/controllers/row/ExternalRequest.ts +47 -16
- package/src/api/controllers/row/external.js +2 -0
- package/src/api/controllers/row/utils.js +8 -11
- package/src/api/controllers/static/index.js +1 -1
- package/src/api/controllers/table/external.js +300 -0
- package/src/api/controllers/table/index.js +47 -137
- package/src/api/controllers/table/internal.js +155 -0
- package/src/api/controllers/table/utils.js +74 -33
- package/src/api/controllers/user.js +132 -13
- package/src/api/routes/query.js +1 -0
- package/src/api/routes/table.js +11 -0
- package/src/api/routes/tests/datasource.spec.js +2 -2
- package/src/api/routes/tests/misc.spec.js +5 -1
- package/src/api/routes/tests/query.spec.js +0 -1
- package/src/api/routes/user.js +5 -0
- package/src/app.ts +2 -10
- package/src/automations/bullboard.js +28 -11
- package/src/automations/tests/automation.spec.js +3 -4
- package/src/automations/triggers.js +7 -3
- package/src/automations/utils.js +12 -2
- package/src/constants/index.js +15 -0
- package/src/definitions/common.ts +7 -1
- package/src/definitions/datasource.ts +9 -3
- package/src/integrations/airtable.ts +2 -1
- package/src/integrations/arangodb.ts +2 -1
- package/src/integrations/base/IntegrationBase.ts +6 -0
- package/src/integrations/base/datasourcePlus.ts +9 -0
- package/src/integrations/base/sql.ts +359 -222
- package/src/integrations/base/sqlTable.ts +180 -0
- package/src/integrations/base/utils.ts +22 -0
- package/src/integrations/couchdb.ts +2 -1
- package/src/integrations/dynamodb.ts +2 -1
- package/src/integrations/elasticsearch.ts +2 -1
- package/src/integrations/microsoftSqlServer.ts +145 -13
- package/src/integrations/mongodb.ts +2 -1
- package/src/integrations/mysql.ts +20 -94
- package/src/integrations/postgres.ts +45 -35
- package/src/integrations/rest.ts +2 -1
- package/src/integrations/s3.ts +2 -1
- package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
- package/src/integrations/tests/postgres.spec.js +4 -0
- package/src/integrations/utils.ts +60 -8
- package/src/middleware/authorized.js +2 -1
- package/src/middleware/builder.js +4 -1
- package/src/tests/utilities/TestConfiguration.js +2 -1
- package/src/{automations/thread.js → threads/automation.js} +24 -7
- package/src/threads/index.js +60 -0
- package/src/threads/query.js +63 -0
- package/src/utilities/csvParser.js +48 -5
- package/src/utilities/global.js +7 -2
- package/src/utilities/index.js +18 -2
- package/src/utilities/queue/inMemoryQueue.js +7 -0
- package/src/utilities/scriptRunner.js +4 -1
- package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
- package/src/utilities/tests/csvParser.spec.js +3 -0
- package/tsconfig.json +1 -1
- package/builder/assets/index.34964222.js +0 -201
- package/builder/assets/index.7469e1e4.css +0 -29
- package/builder/assets/vendor.278a305c.js +0 -9
- package/builder/assets/vendor.d75d5eb7.css +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const { processString } = require("@budibase/string-templates")
|
|
2
2
|
const CouchDB = require("../../db")
|
|
3
3
|
const { generateQueryID, getQueryParams } = require("../../db/utils")
|
|
4
|
-
const { integrations } = require("../../integrations")
|
|
5
4
|
const { BaseQueryVerbs } = require("../../constants")
|
|
6
5
|
const env = require("../../environment")
|
|
7
|
-
const
|
|
6
|
+
const { Thread, ThreadType } = require("../../threads")
|
|
7
|
+
|
|
8
|
+
const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
|
|
8
9
|
|
|
9
10
|
// simple function to append "readable" to all read queries
|
|
10
11
|
function enrichQueries(input) {
|
|
@@ -18,47 +19,6 @@ function enrichQueries(input) {
|
|
|
18
19
|
return wasArray ? queries : queries[0]
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
function formatResponse(resp) {
|
|
22
|
-
if (typeof resp === "string") {
|
|
23
|
-
try {
|
|
24
|
-
resp = JSON.parse(resp)
|
|
25
|
-
} catch (err) {
|
|
26
|
-
resp = { response: resp }
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return resp
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function runAndTransform(
|
|
33
|
-
integration,
|
|
34
|
-
queryVerb,
|
|
35
|
-
enrichedQuery,
|
|
36
|
-
transformer
|
|
37
|
-
) {
|
|
38
|
-
let rows = formatResponse(await integration[queryVerb](enrichedQuery))
|
|
39
|
-
|
|
40
|
-
// transform as required
|
|
41
|
-
if (transformer) {
|
|
42
|
-
const runner = new ScriptRunner(transformer, { data: rows })
|
|
43
|
-
rows = runner.execute()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// needs to an array for next step
|
|
47
|
-
if (!Array.isArray(rows)) {
|
|
48
|
-
rows = [rows]
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// map into JSON if just raw primitive here
|
|
52
|
-
if (rows.find(row => typeof row !== "object")) {
|
|
53
|
-
rows = rows.map(value => ({ value }))
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// get all the potential fields in the schema
|
|
57
|
-
let keys = rows.flatMap(Object.keys)
|
|
58
|
-
|
|
59
|
-
return { rows, keys }
|
|
60
|
-
}
|
|
61
|
-
|
|
62
22
|
exports.fetch = async function (ctx) {
|
|
63
23
|
const db = new CouchDB(ctx.appId)
|
|
64
24
|
|
|
@@ -98,7 +58,9 @@ async function enrichQueryFields(fields, parameters = {}) {
|
|
|
98
58
|
enrichedQuery[key] = await enrichQueryFields(fields[key], parameters)
|
|
99
59
|
} else if (typeof fields[key] === "string") {
|
|
100
60
|
// enrich string value as normal
|
|
101
|
-
enrichedQuery[key] = await processString(fields[key], parameters
|
|
61
|
+
enrichedQuery[key] = await processString(fields[key], parameters, {
|
|
62
|
+
noHelpers: true,
|
|
63
|
+
})
|
|
102
64
|
} else {
|
|
103
65
|
enrichedQuery[key] = fields[key]
|
|
104
66
|
}
|
|
@@ -141,30 +103,23 @@ exports.preview = async function (ctx) {
|
|
|
141
103
|
|
|
142
104
|
const datasource = await db.get(ctx.request.body.datasourceId)
|
|
143
105
|
|
|
144
|
-
const Integration = integrations[datasource.source]
|
|
145
|
-
|
|
146
|
-
if (!Integration) {
|
|
147
|
-
ctx.throw(400, "Integration type does not exist.")
|
|
148
|
-
}
|
|
149
|
-
|
|
150
106
|
const { fields, parameters, queryVerb, transformer } = ctx.request.body
|
|
151
107
|
const enrichedQuery = await enrichQueryFields(fields, parameters)
|
|
152
|
-
const integration = new Integration(datasource.config)
|
|
153
108
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
109
|
+
try {
|
|
110
|
+
const { rows, keys } = await Runner.run({
|
|
111
|
+
datasource,
|
|
112
|
+
queryVerb,
|
|
113
|
+
query: enrichedQuery,
|
|
114
|
+
transformer,
|
|
115
|
+
})
|
|
160
116
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
integration.end()
|
|
117
|
+
ctx.body = {
|
|
118
|
+
rows,
|
|
119
|
+
schemaFields: [...new Set(keys)],
|
|
120
|
+
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
ctx.throw(400, err)
|
|
168
123
|
}
|
|
169
124
|
}
|
|
170
125
|
|
|
@@ -174,29 +129,22 @@ exports.execute = async function (ctx) {
|
|
|
174
129
|
const query = await db.get(ctx.params.queryId)
|
|
175
130
|
const datasource = await db.get(query.datasourceId)
|
|
176
131
|
|
|
177
|
-
const Integration = integrations[datasource.source]
|
|
178
|
-
|
|
179
|
-
if (!Integration) {
|
|
180
|
-
ctx.throw(400, "Integration type does not exist.")
|
|
181
|
-
}
|
|
182
|
-
|
|
183
132
|
const enrichedQuery = await enrichQueryFields(
|
|
184
133
|
query.fields,
|
|
185
134
|
ctx.request.body.parameters
|
|
186
135
|
)
|
|
187
|
-
const integration = new Integration(datasource.config)
|
|
188
136
|
|
|
189
137
|
// call the relevant CRUD method on the integration class
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
138
|
+
try {
|
|
139
|
+
const { rows } = await Runner.run({
|
|
140
|
+
datasource,
|
|
141
|
+
queryVerb: query.queryVerb,
|
|
142
|
+
query: enrichedQuery,
|
|
143
|
+
transformer: query.transformer,
|
|
144
|
+
})
|
|
145
|
+
ctx.body = rows
|
|
146
|
+
} catch (err) {
|
|
147
|
+
ctx.throw(400, err)
|
|
200
148
|
}
|
|
201
149
|
}
|
|
202
150
|
|
|
@@ -33,10 +33,11 @@ interface RunConfig {
|
|
|
33
33
|
sort?: SortJson
|
|
34
34
|
paginate?: PaginationJson
|
|
35
35
|
row?: Row
|
|
36
|
+
rows?: Row[]
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
module External {
|
|
39
|
-
const {
|
|
40
|
+
const { getDatasourceAndQuery } = require("./utils")
|
|
40
41
|
const {
|
|
41
42
|
DataSourceOperation,
|
|
42
43
|
FieldTypes,
|
|
@@ -46,6 +47,7 @@ module External {
|
|
|
46
47
|
const { processObjectSync } = require("@budibase/string-templates")
|
|
47
48
|
const { cloneDeep } = require("lodash/fp")
|
|
48
49
|
const CouchDB = require("../../../db")
|
|
50
|
+
const { processFormulas } = require("../../../utilities/rowProcessor/utils")
|
|
49
51
|
|
|
50
52
|
function buildFilters(
|
|
51
53
|
id: string | undefined,
|
|
@@ -162,8 +164,8 @@ module External {
|
|
|
162
164
|
}
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
function basicProcessing(row: Row, table: Table) {
|
|
166
|
-
const thisRow:
|
|
167
|
+
function basicProcessing(row: Row, table: Table): Row {
|
|
168
|
+
const thisRow: Row = {}
|
|
167
169
|
// filter the row down to what is actually the row (not joined)
|
|
168
170
|
for (let fieldName of Object.keys(table.schema)) {
|
|
169
171
|
const value = row[`${table.name}.${fieldName}`] || row[fieldName]
|
|
@@ -178,6 +180,23 @@ module External {
|
|
|
178
180
|
return thisRow
|
|
179
181
|
}
|
|
180
182
|
|
|
183
|
+
function fixArrayTypes(row: Row, table: Table) {
|
|
184
|
+
for (let [fieldName, schema] of Object.entries(table.schema)) {
|
|
185
|
+
if (
|
|
186
|
+
schema.type === FieldTypes.ARRAY &&
|
|
187
|
+
typeof row[fieldName] === "string"
|
|
188
|
+
) {
|
|
189
|
+
try {
|
|
190
|
+
row[fieldName] = JSON.parse(row[fieldName])
|
|
191
|
+
} catch (err) {
|
|
192
|
+
// couldn't convert back to array, ignore
|
|
193
|
+
delete row[fieldName]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return row
|
|
198
|
+
}
|
|
199
|
+
|
|
181
200
|
function isMany(field: FieldSchema) {
|
|
182
201
|
return (
|
|
183
202
|
field.relationshipType && field.relationshipType.split("-")[0] === "many"
|
|
@@ -225,7 +244,12 @@ module External {
|
|
|
225
244
|
manyRelationships: ManyRelationship[] = []
|
|
226
245
|
for (let [key, field] of Object.entries(table.schema)) {
|
|
227
246
|
// if set already, or not set just skip it
|
|
228
|
-
if (
|
|
247
|
+
if (
|
|
248
|
+
row[key] == null ||
|
|
249
|
+
newRow[key] ||
|
|
250
|
+
field.autocolumn ||
|
|
251
|
+
field.type === FieldTypes.FORMULA
|
|
252
|
+
) {
|
|
229
253
|
continue
|
|
230
254
|
}
|
|
231
255
|
// if its an empty string then it means return the column to null (if possible)
|
|
@@ -336,7 +360,7 @@ module External {
|
|
|
336
360
|
table: Table,
|
|
337
361
|
relationships: RelationshipsJson[]
|
|
338
362
|
) {
|
|
339
|
-
if (rows[0].read === true) {
|
|
363
|
+
if (!rows || rows.length === 0 || rows[0].read === true) {
|
|
340
364
|
return []
|
|
341
365
|
}
|
|
342
366
|
let finalRows: { [key: string]: Row } = {}
|
|
@@ -352,7 +376,10 @@ module External {
|
|
|
352
376
|
)
|
|
353
377
|
continue
|
|
354
378
|
}
|
|
355
|
-
const thisRow = basicProcessing(row, table)
|
|
379
|
+
const thisRow = fixArrayTypes(basicProcessing(row, table), table)
|
|
380
|
+
if (thisRow._id == null) {
|
|
381
|
+
throw "Unable to generate row ID for SQL rows"
|
|
382
|
+
}
|
|
356
383
|
finalRows[thisRow._id] = thisRow
|
|
357
384
|
// do this at end once its been added to the final rows
|
|
358
385
|
finalRows = this.updateRelationshipColumns(
|
|
@@ -361,7 +388,7 @@ module External {
|
|
|
361
388
|
relationships
|
|
362
389
|
)
|
|
363
390
|
}
|
|
364
|
-
return Object.values(finalRows)
|
|
391
|
+
return processFormulas(table, Object.values(finalRows))
|
|
365
392
|
}
|
|
366
393
|
|
|
367
394
|
/**
|
|
@@ -428,7 +455,7 @@ module External {
|
|
|
428
455
|
const tableId = isMany ? field.through : field.tableId
|
|
429
456
|
const manyKey = field.throughFrom || primaryKey
|
|
430
457
|
const fieldName = isMany ? manyKey : field.fieldName
|
|
431
|
-
const response = await
|
|
458
|
+
const response = await getDatasourceAndQuery(this.appId, {
|
|
432
459
|
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
|
|
433
460
|
filters: {
|
|
434
461
|
equal: {
|
|
@@ -479,7 +506,7 @@ module External {
|
|
|
479
506
|
: DataSourceOperation.CREATE
|
|
480
507
|
if (!found) {
|
|
481
508
|
promises.push(
|
|
482
|
-
|
|
509
|
+
getDatasourceAndQuery(appId, {
|
|
483
510
|
endpoint: getEndpoint(tableId, operation),
|
|
484
511
|
// if we're doing many relationships then we're writing, only one response
|
|
485
512
|
body,
|
|
@@ -509,7 +536,7 @@ module External {
|
|
|
509
536
|
: DataSourceOperation.UPDATE
|
|
510
537
|
const body = isMany ? null : { [colName]: null }
|
|
511
538
|
promises.push(
|
|
512
|
-
|
|
539
|
+
getDatasourceAndQuery(this.appId, {
|
|
513
540
|
endpoint: getEndpoint(tableId, op),
|
|
514
541
|
body,
|
|
515
542
|
filters,
|
|
@@ -532,16 +559,17 @@ module External {
|
|
|
532
559
|
table: Table,
|
|
533
560
|
includeRelations: IncludeRelationships = IncludeRelationships.INCLUDE
|
|
534
561
|
) {
|
|
535
|
-
function
|
|
562
|
+
function extractRealFields(table: Table, existing: string[] = []) {
|
|
536
563
|
return Object.entries(table.schema)
|
|
537
564
|
.filter(
|
|
538
565
|
column =>
|
|
539
566
|
column[1].type !== FieldTypes.LINK &&
|
|
567
|
+
column[1].type !== FieldTypes.FORMULA &&
|
|
540
568
|
!existing.find((field: string) => field === column[0])
|
|
541
569
|
)
|
|
542
570
|
.map(column => `${table.name}.${column[0]}`)
|
|
543
571
|
}
|
|
544
|
-
let fields =
|
|
572
|
+
let fields = extractRealFields(table)
|
|
545
573
|
for (let field of Object.values(table.schema)) {
|
|
546
574
|
if (field.type !== FieldTypes.LINK || !includeRelations) {
|
|
547
575
|
continue
|
|
@@ -549,7 +577,7 @@ module External {
|
|
|
549
577
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
|
550
578
|
const linkTable = this.tables[linkTableName]
|
|
551
579
|
if (linkTable) {
|
|
552
|
-
const linkedFields =
|
|
580
|
+
const linkedFields = extractRealFields(linkTable, fields)
|
|
553
581
|
fields = fields.concat(linkedFields)
|
|
554
582
|
}
|
|
555
583
|
}
|
|
@@ -573,7 +601,10 @@ module External {
|
|
|
573
601
|
throw `Unable to process query, table "${tableName}" not defined.`
|
|
574
602
|
}
|
|
575
603
|
// look for specific components of config which may not be considered acceptable
|
|
576
|
-
let { id, row, filters, sort, paginate } = cleanupConfig(
|
|
604
|
+
let { id, row, filters, sort, paginate, rows } = cleanupConfig(
|
|
605
|
+
config,
|
|
606
|
+
table
|
|
607
|
+
)
|
|
577
608
|
filters = buildFilters(id, filters || {}, table)
|
|
578
609
|
const relationships = this.buildRelationships(table)
|
|
579
610
|
// clean up row on ingress using schema
|
|
@@ -599,7 +630,7 @@ module External {
|
|
|
599
630
|
sort,
|
|
600
631
|
paginate,
|
|
601
632
|
relationships,
|
|
602
|
-
body: row,
|
|
633
|
+
body: row || rows,
|
|
603
634
|
// pass an id filter into extra, purely for mysql/returning
|
|
604
635
|
extra: {
|
|
605
636
|
idFilter: buildFilters(id || generateIdForRow(row, table), {}, table),
|
|
@@ -609,7 +640,7 @@ module External {
|
|
|
609
640
|
},
|
|
610
641
|
}
|
|
611
642
|
// can't really use response right now
|
|
612
|
-
const response = await
|
|
643
|
+
const response = await getDatasourceAndQuery(appId, json)
|
|
613
644
|
// handle many to many relationships now if we know the ID (could be auto increment)
|
|
614
645
|
if (
|
|
615
646
|
operation !== DataSourceOperation.READ &&
|
|
@@ -4,8 +4,8 @@ const CouchDB = require("../../../db")
|
|
|
4
4
|
const { InternalTables } = require("../../../db/utils")
|
|
5
5
|
const userController = require("../user")
|
|
6
6
|
const { FieldTypes } = require("../../../constants")
|
|
7
|
-
const { integrations } = require("../../../integrations")
|
|
8
7
|
const { processStringSync } = require("@budibase/string-templates")
|
|
8
|
+
const { makeExternalQuery } = require("../../../integrations/base/utils")
|
|
9
9
|
|
|
10
10
|
validateJs.extend(validateJs.validators.datetime, {
|
|
11
11
|
parse: function (value) {
|
|
@@ -17,18 +17,11 @@ validateJs.extend(validateJs.validators.datetime, {
|
|
|
17
17
|
},
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
exports.
|
|
20
|
+
exports.getDatasourceAndQuery = async (appId, json) => {
|
|
21
21
|
const datasourceId = json.endpoint.datasourceId
|
|
22
22
|
const db = new CouchDB(appId)
|
|
23
23
|
const datasource = await db.get(datasourceId)
|
|
24
|
-
|
|
25
|
-
// query is the opinionated function
|
|
26
|
-
if (Integration.prototype.query) {
|
|
27
|
-
const integration = new Integration(datasource.config)
|
|
28
|
-
return integration.query(json)
|
|
29
|
-
} else {
|
|
30
|
-
throw "Datasource does not support query."
|
|
31
|
-
}
|
|
24
|
+
return makeExternalQuery(datasource, json)
|
|
32
25
|
}
|
|
33
26
|
|
|
34
27
|
exports.findRow = async (ctx, db, tableId, rowId) => {
|
|
@@ -68,7 +61,11 @@ exports.validate = async ({ appId, tableId, row, table }) => {
|
|
|
68
61
|
let res
|
|
69
62
|
|
|
70
63
|
// Validate.js doesn't seem to handle array
|
|
71
|
-
if (
|
|
64
|
+
if (
|
|
65
|
+
table.schema[fieldName].type === FieldTypes.ARRAY &&
|
|
66
|
+
row[fieldName] &&
|
|
67
|
+
row[fieldName].length
|
|
68
|
+
) {
|
|
72
69
|
row[fieldName].map(val => {
|
|
73
70
|
if (!constraints.inclusion.includes(val)) {
|
|
74
71
|
errors[fieldName] = "Field not in list"
|
|
@@ -87,7 +87,7 @@ exports.serveApp = async function (ctx) {
|
|
|
87
87
|
title: appInfo.name,
|
|
88
88
|
production: env.isProd(),
|
|
89
89
|
appId,
|
|
90
|
-
clientLibPath: clientLibraryPath(appId),
|
|
90
|
+
clientLibPath: clientLibraryPath(appId, appInfo.version),
|
|
91
91
|
})
|
|
92
92
|
|
|
93
93
|
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
const CouchDB = require("../../../db")
|
|
2
|
+
const {
|
|
3
|
+
buildExternalTableId,
|
|
4
|
+
breakExternalTableId,
|
|
5
|
+
} = require("../../../integrations/utils")
|
|
6
|
+
const {
|
|
7
|
+
getTable,
|
|
8
|
+
generateForeignKey,
|
|
9
|
+
generateJunctionTableName,
|
|
10
|
+
foreignKeyStructure,
|
|
11
|
+
hasTypeChanged,
|
|
12
|
+
} = require("./utils")
|
|
13
|
+
const {
|
|
14
|
+
DataSourceOperation,
|
|
15
|
+
FieldTypes,
|
|
16
|
+
RelationshipTypes,
|
|
17
|
+
} = require("../../../constants")
|
|
18
|
+
const { makeExternalQuery } = require("../../../integrations/base/utils")
|
|
19
|
+
const { cloneDeep } = require("lodash/fp")
|
|
20
|
+
const csvParser = require("../../../utilities/csvParser")
|
|
21
|
+
const { handleRequest } = require("../row/external")
|
|
22
|
+
|
|
23
|
+
async function makeTableRequest(
|
|
24
|
+
datasource,
|
|
25
|
+
operation,
|
|
26
|
+
table,
|
|
27
|
+
tables,
|
|
28
|
+
oldTable = null
|
|
29
|
+
) {
|
|
30
|
+
const json = {
|
|
31
|
+
endpoint: {
|
|
32
|
+
datasourceId: datasource._id,
|
|
33
|
+
entityId: table._id,
|
|
34
|
+
operation,
|
|
35
|
+
},
|
|
36
|
+
meta: {
|
|
37
|
+
tables,
|
|
38
|
+
},
|
|
39
|
+
table,
|
|
40
|
+
}
|
|
41
|
+
if (oldTable) {
|
|
42
|
+
json.meta.table = oldTable
|
|
43
|
+
}
|
|
44
|
+
return makeExternalQuery(datasource, json)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function cleanupRelationships(table, tables, oldTable = null) {
|
|
48
|
+
const tableToIterate = oldTable ? oldTable : table
|
|
49
|
+
// clean up relationships in couch table schemas
|
|
50
|
+
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
|
51
|
+
if (
|
|
52
|
+
schema.type === FieldTypes.LINK &&
|
|
53
|
+
(!oldTable || table.schema[key] == null)
|
|
54
|
+
) {
|
|
55
|
+
const relatedTable = Object.values(tables).find(
|
|
56
|
+
table => table._id === schema.tableId
|
|
57
|
+
)
|
|
58
|
+
const foreignKey = schema.foreignKey
|
|
59
|
+
if (!relatedTable || !foreignKey) {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
for (let [relatedKey, relatedSchema] of Object.entries(
|
|
63
|
+
relatedTable.schema
|
|
64
|
+
)) {
|
|
65
|
+
if (
|
|
66
|
+
relatedSchema.type === FieldTypes.LINK &&
|
|
67
|
+
relatedSchema.fieldName === foreignKey
|
|
68
|
+
) {
|
|
69
|
+
delete relatedTable.schema[relatedKey]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getDatasourceId(table) {
|
|
77
|
+
if (!table) {
|
|
78
|
+
throw "No table supplied"
|
|
79
|
+
}
|
|
80
|
+
if (table.sourceId) {
|
|
81
|
+
return table.sourceId
|
|
82
|
+
}
|
|
83
|
+
return breakExternalTableId(table._id).datasourceId
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function otherRelationshipType(type) {
|
|
87
|
+
if (type === RelationshipTypes.MANY_TO_MANY) {
|
|
88
|
+
return RelationshipTypes.MANY_TO_MANY
|
|
89
|
+
}
|
|
90
|
+
return type === RelationshipTypes.ONE_TO_MANY
|
|
91
|
+
? RelationshipTypes.MANY_TO_ONE
|
|
92
|
+
: RelationshipTypes.ONE_TO_MANY
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function generateManyLinkSchema(datasource, column, table, relatedTable) {
|
|
96
|
+
const primary = table.name + table.primary[0]
|
|
97
|
+
const relatedPrimary = relatedTable.name + relatedTable.primary[0]
|
|
98
|
+
const jcTblName = generateJunctionTableName(column, table, relatedTable)
|
|
99
|
+
// first create the new table
|
|
100
|
+
const junctionTable = {
|
|
101
|
+
_id: buildExternalTableId(datasource._id, jcTblName),
|
|
102
|
+
name: jcTblName,
|
|
103
|
+
primary: [primary, relatedPrimary],
|
|
104
|
+
constrained: [primary, relatedPrimary],
|
|
105
|
+
schema: {
|
|
106
|
+
[primary]: foreignKeyStructure(primary, {
|
|
107
|
+
toTable: table.name,
|
|
108
|
+
toKey: table.primary[0],
|
|
109
|
+
}),
|
|
110
|
+
[relatedPrimary]: foreignKeyStructure(relatedPrimary, {
|
|
111
|
+
toTable: relatedTable.name,
|
|
112
|
+
toKey: relatedTable.primary[0],
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
column.through = junctionTable._id
|
|
117
|
+
column.throughFrom = primary
|
|
118
|
+
column.throughTo = relatedPrimary
|
|
119
|
+
column.fieldName = relatedPrimary
|
|
120
|
+
return junctionTable
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function generateLinkSchema(column, table, relatedTable, type) {
|
|
124
|
+
const isOneSide = type === RelationshipTypes.ONE_TO_MANY
|
|
125
|
+
const primary = isOneSide ? relatedTable.primary[0] : table.primary[0]
|
|
126
|
+
// generate a foreign key
|
|
127
|
+
const foreignKey = generateForeignKey(column, relatedTable)
|
|
128
|
+
column.relationshipType = type
|
|
129
|
+
column.foreignKey = isOneSide ? foreignKey : primary
|
|
130
|
+
column.fieldName = isOneSide ? primary : foreignKey
|
|
131
|
+
return foreignKey
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function generateRelatedSchema(linkColumn, table, relatedTable, columnName) {
|
|
135
|
+
// generate column for other table
|
|
136
|
+
const relatedSchema = cloneDeep(linkColumn)
|
|
137
|
+
// swap them from the main link
|
|
138
|
+
if (linkColumn.foreignKey) {
|
|
139
|
+
relatedSchema.fieldName = linkColumn.foreignKey
|
|
140
|
+
relatedSchema.foreignKey = linkColumn.fieldName
|
|
141
|
+
}
|
|
142
|
+
// is many to many
|
|
143
|
+
else {
|
|
144
|
+
// don't need to copy through, already got it
|
|
145
|
+
relatedSchema.fieldName = linkColumn.throughFrom
|
|
146
|
+
relatedSchema.throughTo = linkColumn.throughFrom
|
|
147
|
+
relatedSchema.throughFrom = linkColumn.throughTo
|
|
148
|
+
}
|
|
149
|
+
relatedSchema.relationshipType = otherRelationshipType(
|
|
150
|
+
linkColumn.relationshipType
|
|
151
|
+
)
|
|
152
|
+
relatedSchema.tableId = relatedTable._id
|
|
153
|
+
relatedSchema.name = columnName
|
|
154
|
+
table.schema[columnName] = relatedSchema
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isRelationshipSetup(column) {
|
|
158
|
+
return column.foreignKey || column.through
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
exports.save = async function (ctx) {
|
|
162
|
+
const appId = ctx.appId
|
|
163
|
+
const table = ctx.request.body
|
|
164
|
+
// can't do this
|
|
165
|
+
delete table.dataImport
|
|
166
|
+
const datasourceId = getDatasourceId(ctx.request.body)
|
|
167
|
+
let tableToSave = {
|
|
168
|
+
type: "table",
|
|
169
|
+
_id: buildExternalTableId(datasourceId, table.name),
|
|
170
|
+
...table,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let oldTable
|
|
174
|
+
if (ctx.request.body && ctx.request.body._id) {
|
|
175
|
+
oldTable = await getTable(appId, ctx.request.body._id)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (hasTypeChanged(tableToSave, oldTable)) {
|
|
179
|
+
ctx.throw(400, "A column type has changed.")
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const db = new CouchDB(appId)
|
|
183
|
+
const datasource = await db.get(datasourceId)
|
|
184
|
+
const oldTables = cloneDeep(datasource.entities)
|
|
185
|
+
const tables = datasource.entities
|
|
186
|
+
|
|
187
|
+
const extraTablesToUpdate = []
|
|
188
|
+
|
|
189
|
+
// check if relations need setup
|
|
190
|
+
for (let schema of Object.values(tableToSave.schema)) {
|
|
191
|
+
if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
const relatedTable = Object.values(tables).find(
|
|
195
|
+
table => table._id === schema.tableId
|
|
196
|
+
)
|
|
197
|
+
const relatedColumnName = schema.fieldName
|
|
198
|
+
const relationType = schema.relationshipType
|
|
199
|
+
if (relationType === RelationshipTypes.MANY_TO_MANY) {
|
|
200
|
+
const junctionTable = generateManyLinkSchema(
|
|
201
|
+
datasource,
|
|
202
|
+
schema,
|
|
203
|
+
table,
|
|
204
|
+
relatedTable
|
|
205
|
+
)
|
|
206
|
+
if (tables[junctionTable.name]) {
|
|
207
|
+
throw "Junction table already exists, cannot create another relationship."
|
|
208
|
+
}
|
|
209
|
+
tables[junctionTable.name] = junctionTable
|
|
210
|
+
extraTablesToUpdate.push(junctionTable)
|
|
211
|
+
} else {
|
|
212
|
+
const fkTable =
|
|
213
|
+
relationType === RelationshipTypes.ONE_TO_MANY ? table : relatedTable
|
|
214
|
+
const foreignKey = generateLinkSchema(
|
|
215
|
+
schema,
|
|
216
|
+
table,
|
|
217
|
+
relatedTable,
|
|
218
|
+
relationType
|
|
219
|
+
)
|
|
220
|
+
fkTable.schema[foreignKey] = foreignKeyStructure(foreignKey)
|
|
221
|
+
if (fkTable.constrained == null) {
|
|
222
|
+
fkTable.constrained = []
|
|
223
|
+
}
|
|
224
|
+
if (fkTable.constrained.indexOf(foreignKey) === -1) {
|
|
225
|
+
fkTable.constrained.push(foreignKey)
|
|
226
|
+
}
|
|
227
|
+
// foreign key is in other table, need to save it to external
|
|
228
|
+
if (fkTable._id !== table._id) {
|
|
229
|
+
extraTablesToUpdate.push(fkTable)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
generateRelatedSchema(schema, relatedTable, table, relatedColumnName)
|
|
233
|
+
schema.main = true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
cleanupRelationships(tableToSave, tables, oldTable)
|
|
237
|
+
|
|
238
|
+
const operation = oldTable
|
|
239
|
+
? DataSourceOperation.UPDATE_TABLE
|
|
240
|
+
: DataSourceOperation.CREATE_TABLE
|
|
241
|
+
await makeTableRequest(datasource, operation, tableToSave, tables, oldTable)
|
|
242
|
+
// update any extra tables (like foreign keys in other tables)
|
|
243
|
+
for (let extraTable of extraTablesToUpdate) {
|
|
244
|
+
const oldExtraTable = oldTables[extraTable.name]
|
|
245
|
+
let op = oldExtraTable
|
|
246
|
+
? DataSourceOperation.UPDATE_TABLE
|
|
247
|
+
: DataSourceOperation.CREATE_TABLE
|
|
248
|
+
await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// make sure the constrained list, all still exist
|
|
252
|
+
if (Array.isArray(tableToSave.constrained)) {
|
|
253
|
+
tableToSave.constrained = tableToSave.constrained.filter(constraint =>
|
|
254
|
+
Object.keys(tableToSave.schema).includes(constraint)
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// store it into couch now for budibase reference
|
|
259
|
+
datasource.entities[tableToSave.name] = tableToSave
|
|
260
|
+
await db.put(datasource)
|
|
261
|
+
|
|
262
|
+
return tableToSave
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
exports.destroy = async function (ctx) {
|
|
266
|
+
const appId = ctx.appId
|
|
267
|
+
const tableToDelete = await getTable(appId, ctx.params.tableId)
|
|
268
|
+
const datasourceId = getDatasourceId(tableToDelete)
|
|
269
|
+
|
|
270
|
+
const db = new CouchDB(appId)
|
|
271
|
+
const datasource = await db.get(datasourceId)
|
|
272
|
+
const tables = datasource.entities
|
|
273
|
+
|
|
274
|
+
const operation = DataSourceOperation.DELETE_TABLE
|
|
275
|
+
await makeTableRequest(datasource, operation, tableToDelete, tables)
|
|
276
|
+
|
|
277
|
+
cleanupRelationships(tableToDelete, tables)
|
|
278
|
+
|
|
279
|
+
delete datasource.entities[tableToDelete.name]
|
|
280
|
+
await db.put(datasource)
|
|
281
|
+
|
|
282
|
+
return tableToDelete
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
exports.bulkImport = async function (ctx) {
|
|
286
|
+
const appId = ctx.appId
|
|
287
|
+
const table = await getTable(appId, ctx.params.tableId)
|
|
288
|
+
const { dataImport } = ctx.request.body
|
|
289
|
+
if (!dataImport || !dataImport.schema || !dataImport.csvString) {
|
|
290
|
+
ctx.throw(400, "Provided data import information is invalid.")
|
|
291
|
+
}
|
|
292
|
+
const rows = await csvParser.transform({
|
|
293
|
+
...dataImport,
|
|
294
|
+
existingTable: table,
|
|
295
|
+
})
|
|
296
|
+
await handleRequest(appId, DataSourceOperation.BULK_CREATE, table._id, {
|
|
297
|
+
rows,
|
|
298
|
+
})
|
|
299
|
+
return table
|
|
300
|
+
}
|