@budibase/server 1.0.46-alpha.3 → 1.0.46-alpha.7
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.6eae2fa0.js → index.af9e71d8.js} +243 -243
- package/builder/assets/{index.0df4f7ab.css → index.dd9e386f.css} +1 -1
- package/builder/assets/{vendor.10e2ad1b.js → vendor.3bd2f661.js} +1 -1
- package/builder/index.html +3 -3
- package/coverage/clover.xml +542 -551
- package/coverage/coverage-final.json +21 -23
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +179 -95
- 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 +4 -4
- package/coverage/lcov-report/api/controllers/datasource.js.html +58 -22
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +27 -42
- 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/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +15 -15
- package/coverage/lcov-report/api/controllers/query/index.js.html +44 -11
- 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 +5 -5
- package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +5 -14
- 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/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- 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 +10 -10
- 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 +1 -1
- package/coverage/lcov-report/api/routes/index.html +7 -22
- package/coverage/lcov-report/api/routes/index.js.html +4 -10
- 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 +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/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 +17 -2
- 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/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 +1 -1
- package/coverage/lcov-report/automations/index.html +1 -1
- 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/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 +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/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/automations/utils.js.html +5 -5
- package/coverage/lcov-report/constants/index.html +5 -5
- package/coverage/lcov-report/constants/index.js.html +16 -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 +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 +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +2 -2
- package/coverage/lcov-report/db/utils.js.html +17 -17
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- 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 +58 -58
- 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/authorized.js.html +11 -11
- package/coverage/lcov-report/middleware/builder.js.html +8 -8
- package/coverage/lcov-report/middleware/currentapp.js.html +34 -61
- package/coverage/lcov-report/middleware/index.html +10 -10
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/migrations/usageQuotas/index.html +1 -1
- package/coverage/lcov-report/migrations/usageQuotas/index.js.html +1 -1
- package/coverage/lcov-report/migrations/usageQuotas/syncApps.js.html +1 -1
- package/coverage/lcov-report/migrations/usageQuotas/syncRows.js.html +1 -1
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +15 -15
- package/coverage/lcov-report/threads/index.js.html +1 -1
- package/coverage/lcov-report/threads/query.js.html +23 -8
- package/coverage/lcov-report/threads/utils.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/utilities/global.js.html +13 -13
- package/coverage/lcov-report/utilities/index.html +19 -19
- package/coverage/lcov-report/utilities/index.js.html +4 -4
- 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 +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- 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/index.js.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 +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +24 -93
- package/coverage/lcov.info +937 -935
- package/dist/api/controllers/application.js +49 -20
- package/dist/api/controllers/datasource.js +11 -0
- package/dist/api/controllers/query/index.js +11 -3
- package/dist/api/controllers/row/utils.js +1 -2
- package/dist/api/controllers/static/index.js +59 -10
- package/dist/api/routes/index.js +0 -2
- package/dist/api/routes/static.js +2 -1
- package/dist/constants/index.js +3 -0
- package/dist/definitions/datasource.js +1 -0
- package/dist/integrations/googlesheets.js +340 -0
- package/dist/integrations/index.js +3 -0
- package/dist/integrations/utils.js +6 -3
- package/dist/middleware/currentapp.js +0 -9
- package/dist/package.json +8 -5
- package/dist/tests/utilities/TestConfiguration.js +3 -1
- package/dist/threads/query.js +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/workerRequests.js +0 -20
- package/nodemon.json +1 -1
- package/package.json +9 -6
- package/src/api/controllers/application.js +52 -24
- package/src/api/controllers/datasource.js +12 -0
- package/src/api/controllers/query/index.js +14 -3
- package/src/api/controllers/row/utils.js +1 -4
- package/src/api/controllers/static/index.js +63 -10
- package/src/api/routes/index.js +0 -2
- package/src/api/routes/static.js +5 -0
- package/src/api/routes/tests/application.spec.js +2 -2
- package/src/api/routes/tests/static.spec.js +98 -0
- package/src/constants/index.js +4 -0
- package/src/definitions/datasource.ts +3 -0
- package/src/integrations/googlesheets.ts +353 -0
- package/src/integrations/index.ts +3 -0
- package/src/integrations/utils.ts +18 -5
- package/src/middleware/currentapp.js +0 -9
- package/src/migrations/tests/usageQuotas/syncRows.spec.js +1 -1
- package/src/tests/utilities/TestConfiguration.js +3 -1
- package/src/threads/query.js +5 -0
- package/src/utilities/workerRequests.js +0 -23
- package/coverage/lcov-report/api/controllers/hosting.js.html +0 -146
- package/coverage/lcov-report/api/routes/hosting.js.html +0 -119
- package/dist/api/controllers/hosting.js +0 -30
- package/dist/api/routes/hosting.js +0 -11
- package/src/api/controllers/hosting.js +0 -22
- package/src/api/routes/hosting.js +0 -13
- package/src/api/routes/tests/hosting.spec.js +0 -36
|
@@ -61,26 +61,6 @@ exports.sendSmtpEmail = (to, from, subject, contents, automation) => __awaiter(v
|
|
|
61
61
|
}
|
|
62
62
|
return response.json();
|
|
63
63
|
});
|
|
64
|
-
exports.getDeployedApps = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
-
try {
|
|
66
|
-
const response = yield fetch(checkSlashesInUrl(env.WORKER_URL + `/api/apps`), request(null, {
|
|
67
|
-
method: "GET",
|
|
68
|
-
}));
|
|
69
|
-
const json = yield response.json();
|
|
70
|
-
const apps = {};
|
|
71
|
-
for (let [key, value] of Object.entries(json)) {
|
|
72
|
-
if (value.url) {
|
|
73
|
-
value.url = value.url.toLowerCase();
|
|
74
|
-
apps[key.toLowerCase()] = value;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return apps;
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
// error, cannot determine deployed apps, don't stop app creation - sort this later
|
|
81
|
-
return {};
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
64
|
exports.getGlobalSelf = (ctx, appId = null) => __awaiter(void 0, void 0, void 0, function* () {
|
|
85
65
|
const endpoint = `/api/global/users/self`;
|
|
86
66
|
const response = yield fetch(checkSlashesInUrl(env.WORKER_URL + endpoint),
|
package/nodemon.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "1.0.46-alpha.
|
|
4
|
+
"version": "1.0.46-alpha.7",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"license": "GPL-3.0",
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@apidevtools/swagger-parser": "^10.0.3",
|
|
73
|
-
"@budibase/backend-core": "^1.0.46-alpha.
|
|
74
|
-
"@budibase/client": "^1.0.46-alpha.
|
|
75
|
-
"@budibase/string-templates": "^1.0.46-alpha.
|
|
73
|
+
"@budibase/backend-core": "^1.0.46-alpha.7",
|
|
74
|
+
"@budibase/client": "^1.0.46-alpha.7",
|
|
75
|
+
"@budibase/string-templates": "^1.0.46-alpha.7",
|
|
76
76
|
"@bull-board/api": "^3.7.0",
|
|
77
77
|
"@bull-board/koa": "^3.7.0",
|
|
78
78
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -92,6 +92,8 @@
|
|
|
92
92
|
"fix-path": "3.0.0",
|
|
93
93
|
"form-data": "^4.0.0",
|
|
94
94
|
"fs-extra": "8.1.0",
|
|
95
|
+
"google-auth-library": "^7.11.0",
|
|
96
|
+
"google-spreadsheet": "^3.2.0",
|
|
95
97
|
"jimp": "0.16.1",
|
|
96
98
|
"joi": "17.2.1",
|
|
97
99
|
"js-yaml": "^4.1.0",
|
|
@@ -110,7 +112,7 @@
|
|
|
110
112
|
"mongodb": "3.6.3",
|
|
111
113
|
"mssql": "6.2.3",
|
|
112
114
|
"mysql2": "^2.3.1",
|
|
113
|
-
"node-fetch": "2.6.
|
|
115
|
+
"node-fetch": "2.6.7",
|
|
114
116
|
"open": "^8.4.0",
|
|
115
117
|
"pg": "8.5.1",
|
|
116
118
|
"pino-pretty": "4.0.0",
|
|
@@ -139,6 +141,7 @@
|
|
|
139
141
|
"@jest/test-sequencer": "^24.8.0",
|
|
140
142
|
"@types/apidoc": "^0.50.0",
|
|
141
143
|
"@types/bull": "^3.15.1",
|
|
144
|
+
"@types/google-spreadsheet": "^3.1.5",
|
|
142
145
|
"@types/jest": "^26.0.23",
|
|
143
146
|
"@types/koa": "^2.13.3",
|
|
144
147
|
"@types/koa-router": "^7.4.2",
|
|
@@ -165,5 +168,5 @@
|
|
|
165
168
|
"optionalDependencies": {
|
|
166
169
|
"oracledb": "^5.3.0"
|
|
167
170
|
},
|
|
168
|
-
"gitHead": "
|
|
171
|
+
"gitHead": "5718d4925a31131daadf694e7f0ba837084a61f2"
|
|
169
172
|
}
|
|
@@ -33,10 +33,7 @@ const {
|
|
|
33
33
|
Replication,
|
|
34
34
|
} = require("@budibase/backend-core/db")
|
|
35
35
|
const { USERS_TABLE_SCHEMA } = require("../../constants")
|
|
36
|
-
const {
|
|
37
|
-
getDeployedApps,
|
|
38
|
-
removeAppFromUserRoles,
|
|
39
|
-
} = require("../../utilities/workerRequests")
|
|
36
|
+
const { removeAppFromUserRoles } = require("../../utilities/workerRequests")
|
|
40
37
|
const { clientLibraryPath, stringToReadStream } = require("../../utilities")
|
|
41
38
|
const { getAllLocks } = require("../../utilities/redis")
|
|
42
39
|
const {
|
|
@@ -78,29 +75,42 @@ function getUserRoleId(ctx) {
|
|
|
78
75
|
: ctx.user.role._id
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
async function
|
|
78
|
+
async function getAppUrl(ctx) {
|
|
79
|
+
// construct the url
|
|
82
80
|
let url
|
|
83
81
|
if (ctx.request.body.url) {
|
|
82
|
+
// if the url is provided, use that
|
|
84
83
|
url = encodeURI(ctx.request.body.url)
|
|
85
84
|
} else if (ctx.request.body.name) {
|
|
85
|
+
// otherwise use the name
|
|
86
86
|
url = encodeURI(`${ctx.request.body.name}`)
|
|
87
87
|
}
|
|
88
88
|
if (url) {
|
|
89
89
|
url = `/${url.replace(URL_REGEX_SLASH, "")}`.toLowerCase()
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
return url
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const checkAppUrl = (ctx, apps, url, currentAppId) => {
|
|
95
|
+
if (currentAppId) {
|
|
96
|
+
apps = apps.filter(app => app.appId !== currentAppId)
|
|
93
97
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
if (apps.some(app => app.url === url)) {
|
|
99
|
+
ctx.throw(400, "App URL is already in use.")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const checkAppName = (ctx, apps, name, currentAppId) => {
|
|
104
|
+
// TODO: Replace with Joi
|
|
105
|
+
if (!name) {
|
|
106
|
+
ctx.throw(400, "Name is required")
|
|
107
|
+
}
|
|
108
|
+
if (currentAppId) {
|
|
109
|
+
apps = apps.filter(app => app.appId !== currentAppId)
|
|
110
|
+
}
|
|
111
|
+
if (apps.some(app => app.name === name)) {
|
|
112
|
+
ctx.throw(400, "App name is already in use.")
|
|
102
113
|
}
|
|
103
|
-
return url
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
async function createInstance(template) {
|
|
@@ -206,6 +216,12 @@ exports.fetchAppPackage = async ctx => {
|
|
|
206
216
|
}
|
|
207
217
|
|
|
208
218
|
exports.create = async ctx => {
|
|
219
|
+
const apps = await getAllApps(CouchDB, { dev: true })
|
|
220
|
+
const name = ctx.request.body.name
|
|
221
|
+
checkAppName(ctx, apps, name)
|
|
222
|
+
const url = await getAppUrl(ctx)
|
|
223
|
+
checkAppUrl(ctx, apps, url)
|
|
224
|
+
|
|
209
225
|
const { useTemplate, templateKey, templateString } = ctx.request.body
|
|
210
226
|
const instanceConfig = {
|
|
211
227
|
useTemplate,
|
|
@@ -218,7 +234,6 @@ exports.create = async ctx => {
|
|
|
218
234
|
const instance = await createInstance(instanceConfig)
|
|
219
235
|
const appId = instance._id
|
|
220
236
|
|
|
221
|
-
const url = await getAppUrlIfNotInUse(ctx)
|
|
222
237
|
const db = new CouchDB(appId)
|
|
223
238
|
let _rev
|
|
224
239
|
try {
|
|
@@ -235,7 +250,7 @@ exports.create = async ctx => {
|
|
|
235
250
|
type: "app",
|
|
236
251
|
version: packageJson.version,
|
|
237
252
|
componentLibraries: ["@budibase/standard-components"],
|
|
238
|
-
name:
|
|
253
|
+
name: name,
|
|
239
254
|
url: url,
|
|
240
255
|
template: ctx.request.body.template,
|
|
241
256
|
instance: instance,
|
|
@@ -262,8 +277,22 @@ exports.create = async ctx => {
|
|
|
262
277
|
ctx.body = newApplication
|
|
263
278
|
}
|
|
264
279
|
|
|
280
|
+
// This endpoint currently operates as a PATCH rather than a PUT
|
|
281
|
+
// Thus name and url fields are handled only if present
|
|
265
282
|
exports.update = async ctx => {
|
|
266
|
-
const
|
|
283
|
+
const apps = await getAllApps(CouchDB, { dev: true })
|
|
284
|
+
// validation
|
|
285
|
+
const name = ctx.request.body.name
|
|
286
|
+
if (name) {
|
|
287
|
+
checkAppName(ctx, apps, name, ctx.params.appId)
|
|
288
|
+
}
|
|
289
|
+
const url = await getAppUrl(ctx)
|
|
290
|
+
if (url) {
|
|
291
|
+
checkAppUrl(ctx, apps, url, ctx.params.appId)
|
|
292
|
+
ctx.request.body.url = url
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const data = await updateAppPackage(ctx.request.body, ctx.params.appId)
|
|
267
296
|
ctx.status = 200
|
|
268
297
|
ctx.body = data
|
|
269
298
|
}
|
|
@@ -285,7 +314,7 @@ exports.updateClient = async ctx => {
|
|
|
285
314
|
version: packageJson.version,
|
|
286
315
|
revertableVersion: currentVersion,
|
|
287
316
|
}
|
|
288
|
-
const data = await updateAppPackage(
|
|
317
|
+
const data = await updateAppPackage(appPackageUpdates, ctx.params.appId)
|
|
289
318
|
ctx.status = 200
|
|
290
319
|
ctx.body = data
|
|
291
320
|
}
|
|
@@ -308,7 +337,7 @@ exports.revertClient = async ctx => {
|
|
|
308
337
|
version: application.revertableVersion,
|
|
309
338
|
revertableVersion: null,
|
|
310
339
|
}
|
|
311
|
-
const data = await updateAppPackage(
|
|
340
|
+
const data = await updateAppPackage(appPackageUpdates, ctx.params.appId)
|
|
312
341
|
ctx.status = 200
|
|
313
342
|
ctx.body = data
|
|
314
343
|
}
|
|
@@ -381,12 +410,11 @@ exports.sync = async (ctx, next) => {
|
|
|
381
410
|
}
|
|
382
411
|
}
|
|
383
412
|
|
|
384
|
-
const updateAppPackage = async (
|
|
385
|
-
const url = await getAppUrlIfNotInUse(ctx)
|
|
413
|
+
const updateAppPackage = async (appPackage, appId) => {
|
|
386
414
|
const db = new CouchDB(appId)
|
|
387
415
|
const application = await db.get(DocumentTypes.APP_METADATA)
|
|
388
416
|
|
|
389
|
-
const newAppPackage = { ...application, ...appPackage
|
|
417
|
+
const newAppPackage = { ...application, ...appPackage }
|
|
390
418
|
if (appPackage._rev !== application._rev) {
|
|
391
419
|
newAppPackage._rev = application._rev
|
|
392
420
|
}
|
|
@@ -38,6 +38,13 @@ exports.fetch = async function (ctx) {
|
|
|
38
38
|
)
|
|
39
39
|
).rows.map(row => row.doc)
|
|
40
40
|
|
|
41
|
+
for (let datasource of datasources) {
|
|
42
|
+
if (datasource.config && datasource.config.auth) {
|
|
43
|
+
// strip secrets from response so they don't show in the network request
|
|
44
|
+
delete datasource.config.auth
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
ctx.body = [bbInternalDb, ...datasources]
|
|
42
49
|
}
|
|
43
50
|
|
|
@@ -94,8 +101,13 @@ exports.update = async function (ctx) {
|
|
|
94
101
|
const db = new CouchDB(ctx.appId)
|
|
95
102
|
const datasourceId = ctx.params.datasourceId
|
|
96
103
|
let datasource = await db.get(datasourceId)
|
|
104
|
+
const auth = datasource.config.auth
|
|
97
105
|
await invalidateVariables(datasource, ctx.request.body)
|
|
98
106
|
datasource = { ...datasource, ...ctx.request.body }
|
|
107
|
+
if (auth && !ctx.request.body.auth) {
|
|
108
|
+
// don't strip auth config from DB
|
|
109
|
+
datasource.config.auth = auth
|
|
110
|
+
}
|
|
99
111
|
|
|
100
112
|
const response = await db.put(datasource)
|
|
101
113
|
datasource._rev = response.rev
|
|
@@ -141,6 +141,16 @@ async function execute(ctx, opts = { rowsOnly: false }) {
|
|
|
141
141
|
const query = await db.get(ctx.params.queryId)
|
|
142
142
|
const datasource = await db.get(query.datasourceId)
|
|
143
143
|
|
|
144
|
+
const enrichedParameters = ctx.request.body.parameters || {}
|
|
145
|
+
// make sure parameters are fully enriched with defaults
|
|
146
|
+
if (query && query.parameters) {
|
|
147
|
+
for (let parameter of query.parameters) {
|
|
148
|
+
if (!enrichedParameters[parameter.name]) {
|
|
149
|
+
enrichedParameters[parameter.name] = parameter.default
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
144
154
|
// call the relevant CRUD method on the integration class
|
|
145
155
|
try {
|
|
146
156
|
const { rows, pagination, extra } = await Runner.run({
|
|
@@ -149,7 +159,7 @@ async function execute(ctx, opts = { rowsOnly: false }) {
|
|
|
149
159
|
queryVerb: query.queryVerb,
|
|
150
160
|
fields: query.fields,
|
|
151
161
|
pagination: ctx.request.body.pagination,
|
|
152
|
-
parameters:
|
|
162
|
+
parameters: enrichedParameters,
|
|
153
163
|
transformer: query.transformer,
|
|
154
164
|
queryId: ctx.params.queryId,
|
|
155
165
|
})
|
|
@@ -178,8 +188,9 @@ const removeDynamicVariables = async (db, queryId) => {
|
|
|
178
188
|
|
|
179
189
|
if (dynamicVariables) {
|
|
180
190
|
// delete dynamic variables from the datasource
|
|
181
|
-
|
|
182
|
-
|
|
191
|
+
datasource.config.dynamicVariables = dynamicVariables.filter(
|
|
192
|
+
dv => dv.queryId !== queryId
|
|
193
|
+
)
|
|
183
194
|
await db.put(datasource)
|
|
184
195
|
|
|
185
196
|
// invalidate the deleted variables
|
|
@@ -52,10 +52,7 @@ exports.validate = async ({ appId, tableId, row, table }) => {
|
|
|
52
52
|
const constraints = cloneDeep(table.schema[fieldName].constraints)
|
|
53
53
|
const type = table.schema[fieldName].type
|
|
54
54
|
// special case for options, need to always allow unselected (null)
|
|
55
|
-
if (
|
|
56
|
-
(type === FieldTypes.OPTIONS || type === FieldTypes.ARRAY) &&
|
|
57
|
-
constraints.inclusion
|
|
58
|
-
) {
|
|
55
|
+
if (type === FieldTypes.OPTIONS && constraints.inclusion) {
|
|
59
56
|
constraints.inclusion.push(null)
|
|
60
57
|
}
|
|
61
58
|
let res
|
|
@@ -5,7 +5,7 @@ const { resolve, join } = require("../../../utilities/centralPath")
|
|
|
5
5
|
const uuid = require("uuid")
|
|
6
6
|
const { ObjectStoreBuckets } = require("../../../constants")
|
|
7
7
|
const { processString } = require("@budibase/string-templates")
|
|
8
|
-
const {
|
|
8
|
+
const { getAllApps } = require("@budibase/backend-core/db")
|
|
9
9
|
const CouchDB = require("../../../db")
|
|
10
10
|
const {
|
|
11
11
|
loadHandlebarsFile,
|
|
@@ -17,6 +17,8 @@ const { clientLibraryPath } = require("../../../utilities")
|
|
|
17
17
|
const { upload } = require("../../../utilities/fileSystem")
|
|
18
18
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
|
19
19
|
const { DocumentTypes } = require("../../../db/utils")
|
|
20
|
+
const AWS = require("aws-sdk")
|
|
21
|
+
const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1"
|
|
20
22
|
|
|
21
23
|
async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
|
22
24
|
const response = await upload({
|
|
@@ -37,12 +39,18 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
async function
|
|
41
|
-
// the "appId" component of the URL
|
|
42
|
+
async function getAppIdFromUrl(ctx) {
|
|
43
|
+
// the "appId" component of the URL can be the id or the custom url
|
|
42
44
|
let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
// search prod apps for a url that matches, exclude dev where id is always used
|
|
47
|
+
const apps = await getAllApps(CouchDB, { dev: false })
|
|
48
|
+
const app = apps.filter(
|
|
49
|
+
a => a.url && a.url.toLowerCase() === possibleAppUrl
|
|
50
|
+
)[0]
|
|
51
|
+
|
|
52
|
+
if (app && app.appId) {
|
|
53
|
+
return app.appId
|
|
46
54
|
} else {
|
|
47
55
|
return ctx.params.appId
|
|
48
56
|
}
|
|
@@ -75,10 +83,7 @@ exports.uploadFile = async function (ctx) {
|
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
exports.serveApp = async function (ctx) {
|
|
78
|
-
let appId = ctx
|
|
79
|
-
if (env.SELF_HOSTED) {
|
|
80
|
-
appId = await checkForSelfHostedURL(ctx)
|
|
81
|
-
}
|
|
86
|
+
let appId = await getAppIdFromUrl(ctx)
|
|
82
87
|
const App = require("./templates/BudibaseApp.svelte").default
|
|
83
88
|
const db = new CouchDB(appId, { skip_setup: true })
|
|
84
89
|
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
|
@@ -104,3 +109,51 @@ exports.serveClientLibrary = async function (ctx) {
|
|
|
104
109
|
root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),
|
|
105
110
|
})
|
|
106
111
|
}
|
|
112
|
+
|
|
113
|
+
exports.getSignedUploadURL = async function (ctx) {
|
|
114
|
+
const database = new CouchDB(ctx.appId)
|
|
115
|
+
|
|
116
|
+
// Ensure datasource is valid
|
|
117
|
+
let datasource
|
|
118
|
+
try {
|
|
119
|
+
const { datasourceId } = ctx.params
|
|
120
|
+
datasource = await database.get(datasourceId)
|
|
121
|
+
if (!datasource) {
|
|
122
|
+
ctx.throw(400, "The specified datasource could not be found")
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
ctx.throw(400, "The specified datasource could not be found")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Ensure we aren't using a custom endpoint
|
|
129
|
+
if (datasource?.config?.endpoint) {
|
|
130
|
+
ctx.throw(400, "S3 datasources with custom endpoints are not supported")
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Determine type of datasource and generate signed URL
|
|
134
|
+
let signedUrl
|
|
135
|
+
let publicUrl
|
|
136
|
+
if (datasource.source === "S3") {
|
|
137
|
+
const { bucket, key } = ctx.request.body || {}
|
|
138
|
+
if (!bucket || !key) {
|
|
139
|
+
ctx.throw(400, "bucket and key values are required")
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const s3 = new AWS.S3({
|
|
144
|
+
region: AWS_REGION,
|
|
145
|
+
accessKeyId: datasource?.config?.accessKeyId,
|
|
146
|
+
secretAccessKey: datasource?.config?.secretAccessKey,
|
|
147
|
+
apiVersion: "2006-03-01",
|
|
148
|
+
signatureVersion: "v4",
|
|
149
|
+
})
|
|
150
|
+
const params = { Bucket: bucket, Key: key }
|
|
151
|
+
signedUrl = s3.getSignedUrl("putObject", params)
|
|
152
|
+
publicUrl = `https://${bucket}.s3.${AWS_REGION}.amazonaws.com/${key}`
|
|
153
|
+
} catch (error) {
|
|
154
|
+
ctx.throw(400, error)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ctx.body = { signedUrl, publicUrl }
|
|
159
|
+
}
|
package/src/api/routes/index.js
CHANGED
|
@@ -20,7 +20,6 @@ const integrationRoutes = require("./integration")
|
|
|
20
20
|
const permissionRoutes = require("./permission")
|
|
21
21
|
const datasourceRoutes = require("./datasource")
|
|
22
22
|
const queryRoutes = require("./query")
|
|
23
|
-
const hostingRoutes = require("./hosting")
|
|
24
23
|
const backupRoutes = require("./backup")
|
|
25
24
|
const metadataRoutes = require("./metadata")
|
|
26
25
|
const devRoutes = require("./dev")
|
|
@@ -46,7 +45,6 @@ exports.mainRoutes = [
|
|
|
46
45
|
permissionRoutes,
|
|
47
46
|
datasourceRoutes,
|
|
48
47
|
queryRoutes,
|
|
49
|
-
hostingRoutes,
|
|
50
48
|
backupRoutes,
|
|
51
49
|
metadataRoutes,
|
|
52
50
|
devRoutes,
|
package/src/api/routes/static.js
CHANGED
|
@@ -46,5 +46,10 @@ router
|
|
|
46
46
|
)
|
|
47
47
|
// TODO: this likely needs to be secured in some way
|
|
48
48
|
.get("/:appId/:path*", controller.serveApp)
|
|
49
|
+
.post(
|
|
50
|
+
"/api/attachments/:datasourceId/url",
|
|
51
|
+
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
|
52
|
+
controller.getSignedUploadURL
|
|
53
|
+
)
|
|
49
54
|
|
|
50
55
|
module.exports = router
|
|
@@ -53,8 +53,8 @@ describe("/applications", () => {
|
|
|
53
53
|
|
|
54
54
|
describe("fetch", () => {
|
|
55
55
|
it("lists all applications", async () => {
|
|
56
|
-
await config.createApp(
|
|
57
|
-
await config.createApp(
|
|
56
|
+
await config.createApp("app1")
|
|
57
|
+
await config.createApp("app2")
|
|
58
58
|
|
|
59
59
|
const res = await request
|
|
60
60
|
.get(`/api/applications?status=${AppStatus.DEV}`)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
jest.mock("node-fetch")
|
|
2
|
+
jest.mock("aws-sdk", () => ({
|
|
3
|
+
config: {
|
|
4
|
+
update: jest.fn(),
|
|
5
|
+
},
|
|
6
|
+
DynamoDB: {
|
|
7
|
+
DocumentClient: jest.fn(),
|
|
8
|
+
},
|
|
9
|
+
S3: jest.fn(() => ({
|
|
10
|
+
getSignedUrl: jest.fn(() => {
|
|
11
|
+
return "my-url"
|
|
12
|
+
}),
|
|
13
|
+
})),
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
const setup = require("./utilities")
|
|
17
|
+
|
|
18
|
+
describe("/attachments", () => {
|
|
19
|
+
let request = setup.getRequest()
|
|
20
|
+
let config = setup.getConfig()
|
|
21
|
+
let app
|
|
22
|
+
|
|
23
|
+
afterAll(setup.afterAll)
|
|
24
|
+
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
app = await config.init()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe("generateSignedUrls", () => {
|
|
30
|
+
let datasource
|
|
31
|
+
|
|
32
|
+
beforeEach(async () => {
|
|
33
|
+
datasource = await config.createDatasource({
|
|
34
|
+
datasource: {
|
|
35
|
+
type: "datasource",
|
|
36
|
+
name: "Test",
|
|
37
|
+
source: "S3",
|
|
38
|
+
config: {},
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("should be able to generate a signed upload URL", async () => {
|
|
44
|
+
const bucket = "foo"
|
|
45
|
+
const key = "bar"
|
|
46
|
+
const res = await request
|
|
47
|
+
.post(`/api/attachments/${datasource._id}/url`)
|
|
48
|
+
.send({ bucket, key })
|
|
49
|
+
.set(config.defaultHeaders())
|
|
50
|
+
.expect("Content-Type", /json/)
|
|
51
|
+
.expect(200)
|
|
52
|
+
expect(res.body.signedUrl).toEqual("my-url")
|
|
53
|
+
expect(res.body.publicUrl).toEqual(
|
|
54
|
+
`https://${bucket}.s3.eu-west-1.amazonaws.com/${key}`
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("should handle an invalid datasource ID", async () => {
|
|
59
|
+
const res = await request
|
|
60
|
+
.post(`/api/attachments/foo/url`)
|
|
61
|
+
.send({
|
|
62
|
+
bucket: "foo",
|
|
63
|
+
key: "bar",
|
|
64
|
+
})
|
|
65
|
+
.set(config.defaultHeaders())
|
|
66
|
+
.expect("Content-Type", /json/)
|
|
67
|
+
.expect(400)
|
|
68
|
+
expect(res.body.message).toEqual(
|
|
69
|
+
"The specified datasource could not be found"
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it("should require a bucket parameter", async () => {
|
|
74
|
+
const res = await request
|
|
75
|
+
.post(`/api/attachments/${datasource._id}/url`)
|
|
76
|
+
.send({
|
|
77
|
+
bucket: undefined,
|
|
78
|
+
key: "bar",
|
|
79
|
+
})
|
|
80
|
+
.set(config.defaultHeaders())
|
|
81
|
+
.expect("Content-Type", /json/)
|
|
82
|
+
.expect(400)
|
|
83
|
+
expect(res.body.message).toEqual("bucket and key values are required")
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it("should require a key parameter", async () => {
|
|
87
|
+
const res = await request
|
|
88
|
+
.post(`/api/attachments/${datasource._id}/url`)
|
|
89
|
+
.send({
|
|
90
|
+
bucket: "foo",
|
|
91
|
+
})
|
|
92
|
+
.set(config.defaultHeaders())
|
|
93
|
+
.expect("Content-Type", /json/)
|
|
94
|
+
.expect(400)
|
|
95
|
+
expect(res.body.message).toEqual("bucket and key values are required")
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
package/src/constants/index.js
CHANGED
|
@@ -47,6 +47,7 @@ export enum SourceNames {
|
|
|
47
47
|
ARANGODB = "ARANGODB",
|
|
48
48
|
REST = "REST",
|
|
49
49
|
ORACLE = "ORACLE",
|
|
50
|
+
GOOGLE_SHEETS = "GOOGLE_SHEETS",
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export enum IncludeRelationships {
|
|
@@ -86,6 +87,8 @@ export interface ExtraQueryConfig {
|
|
|
86
87
|
export interface Integration {
|
|
87
88
|
docs: string
|
|
88
89
|
plus?: boolean
|
|
90
|
+
auth?: { type: string }
|
|
91
|
+
relationships?: boolean
|
|
89
92
|
description: string
|
|
90
93
|
friendlyName: string
|
|
91
94
|
datasource: {}
|