@budibase/server 0.9.169-alpha.0 → 0.9.169-alpha.4
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.bcacf476.css → index.0ea76a5c.css} +2 -2
- package/builder/assets/{index.cb5f4e3c.js → index.45b6b02b.js} +34 -34
- package/builder/assets/{vendor.bca5cd21.js → vendor.2053eff2.js} +1 -1
- package/builder/index.html +3 -3
- package/coverage/clover.xml +12 -12
- package/coverage/coverage-final.json +2 -2
- 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 +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 +6 -9
- 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/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +5 -5
- 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 +7 -4
- package/coverage/lcov-report/api/controllers/query.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 +1 -1
- 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 +1 -1
- 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/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.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 +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 +1 -1
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- 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 +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 +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/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/thread.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 +1 -1
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/db/utils.js.html +1 -1
- 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 +5 -5
- 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 +1 -1
- package/coverage/lcov-report/middleware/builder.js.html +1 -1
- package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
- 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/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/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 +1 -1
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
- 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/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +12 -11
- package/dist/api/controllers/backup.js +4 -4
- package/dist/api/controllers/permission.js +1 -0
- package/dist/api/controllers/row/ExternalRequest.js +46 -5
- package/dist/integrations/utils.js +16 -1
- package/dist/package.json +4 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/index.js +49 -33
- package/package.json +5 -5
- package/src/api/controllers/backup.js +4 -5
- package/src/api/controllers/permission.js +1 -0
- package/src/api/controllers/row/ExternalRequest.ts +49 -4
- package/src/integrations/utils.ts +15 -0
- package/src/utilities/fileSystem/index.js +65 -43
|
@@ -98,53 +98,69 @@ exports.apiFileReturn = contents => {
|
|
|
98
98
|
fs.writeFileSync(path, contents);
|
|
99
99
|
return fs.createReadStream(path);
|
|
100
100
|
};
|
|
101
|
+
/**
|
|
102
|
+
* Local utility to back up the database state for an app, excluding global user
|
|
103
|
+
* data or user relationships.
|
|
104
|
+
* @param {string} appId The app to backup
|
|
105
|
+
* @param {object} config Config to send to export DB
|
|
106
|
+
* @returns {*} either a string or a stream of the backup
|
|
107
|
+
*/
|
|
108
|
+
const backupAppData = (appId, config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
109
|
+
return yield exports.exportDB(appId, Object.assign(Object.assign({}, config), { filter: doc => !(doc._id.includes(USER_METDATA_PREFIX) ||
|
|
110
|
+
doc._id.includes(LINK_USER_METADATA_PREFIX)) }));
|
|
111
|
+
});
|
|
101
112
|
/**
|
|
102
113
|
* Takes a copy of the database state for an app to the object store.
|
|
103
114
|
* @param {string} appId The ID of the app which is to be backed up.
|
|
104
115
|
* @param {string} backupName The name of the backup located in the object store.
|
|
105
|
-
* @return
|
|
106
|
-
* to the temporary backup file (to return via API if required).
|
|
116
|
+
* @return {*} a readable stream to the completed backup file
|
|
107
117
|
*/
|
|
108
118
|
exports.performBackup = (appId, backupName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
109
|
-
return
|
|
110
|
-
exportName: backupName,
|
|
111
|
-
filter: doc => !(doc._id.includes(USER_METDATA_PREFIX) ||
|
|
112
|
-
doc.includes(LINK_USER_METADATA_PREFIX)),
|
|
113
|
-
});
|
|
119
|
+
return yield backupAppData(appId, { exportName: backupName });
|
|
114
120
|
});
|
|
115
121
|
/**
|
|
116
|
-
*
|
|
122
|
+
* Streams a backup of the database state for an app
|
|
123
|
+
* @param {string} appId The ID of the app which is to be backed up.
|
|
124
|
+
* @returns {*} a readable stream of the backup which is written in real time
|
|
125
|
+
*/
|
|
126
|
+
exports.streamBackup = (appId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
return yield backupAppData(appId, { stream: true });
|
|
128
|
+
});
|
|
129
|
+
/**
|
|
130
|
+
* Exports a DB to either file or a variable (memory).
|
|
117
131
|
* @param {string} dbName the DB which is to be exported.
|
|
118
|
-
* @param {string} exportName optional -
|
|
132
|
+
* @param {string} exportName optional - provide a filename to write the backup to a file
|
|
133
|
+
* @param {boolean} stream optional - whether to perform a full backup
|
|
119
134
|
* @param {function} filter optional - a filter function to clear out any un-wanted docs.
|
|
120
|
-
* @return
|
|
135
|
+
* @return {*} either a readable stream or a string
|
|
121
136
|
*/
|
|
122
|
-
exports.exportDB = (dbName, {
|
|
123
|
-
let stream, appString = "", path = null;
|
|
124
|
-
if (exportName) {
|
|
125
|
-
path = join(budibaseTempDir(), exportName);
|
|
126
|
-
stream = fs.createWriteStream(path);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
stream = new MemoryStream();
|
|
130
|
-
stream.on("data", chunk => {
|
|
131
|
-
appString += chunk.toString();
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
// perform couch dump
|
|
137
|
+
exports.exportDB = (dbName, { stream, filter, exportName } = {}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
135
138
|
const instanceDb = new CouchDB(dbName);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return appString;
|
|
139
|
+
// Stream the dump if required
|
|
140
|
+
if (stream) {
|
|
141
|
+
const memStream = new MemoryStream();
|
|
142
|
+
instanceDb.dump(memStream, { filter });
|
|
143
|
+
return memStream;
|
|
142
144
|
}
|
|
143
|
-
//
|
|
144
|
-
if (
|
|
145
|
-
|
|
145
|
+
// Write the dump to file if required
|
|
146
|
+
if (exportName) {
|
|
147
|
+
const path = join(budibaseTempDir(), exportName);
|
|
148
|
+
const writeStream = fs.createWriteStream(path);
|
|
149
|
+
yield instanceDb.dump(writeStream, { filter });
|
|
150
|
+
// Upload the dump to the object store if self hosted
|
|
151
|
+
if (env.SELF_HOSTED) {
|
|
152
|
+
yield streamUpload(ObjectStoreBuckets.BACKUPS, join(dbName, exportName), fs.createReadStream(path));
|
|
153
|
+
}
|
|
154
|
+
return fs.createReadStream(path);
|
|
146
155
|
}
|
|
147
|
-
|
|
156
|
+
// Stringify the dump in memory if required
|
|
157
|
+
const memStream = new MemoryStream();
|
|
158
|
+
let appString = "";
|
|
159
|
+
memStream.on("data", chunk => {
|
|
160
|
+
appString += chunk.toString();
|
|
161
|
+
});
|
|
162
|
+
yield instanceDb.dump(memStream, { filter });
|
|
163
|
+
return appString;
|
|
148
164
|
});
|
|
149
165
|
/**
|
|
150
166
|
* Writes the provided contents to a temporary file, which can be used briefly.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "0.9.169-alpha.
|
|
4
|
+
"version": "0.9.169-alpha.4",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"repository": {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
"author": "Budibase",
|
|
69
69
|
"license": "AGPL-3.0-or-later",
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@budibase/auth": "^0.9.169-alpha.
|
|
72
|
-
"@budibase/client": "^0.9.169-alpha.
|
|
73
|
-
"@budibase/string-templates": "^0.9.169-alpha.
|
|
71
|
+
"@budibase/auth": "^0.9.169-alpha.4",
|
|
72
|
+
"@budibase/client": "^0.9.169-alpha.4",
|
|
73
|
+
"@budibase/string-templates": "^0.9.169-alpha.4",
|
|
74
74
|
"@elastic/elasticsearch": "7.10.0",
|
|
75
75
|
"@koa/router": "8.0.0",
|
|
76
76
|
"@sendgrid/mail": "7.1.1",
|
|
@@ -149,5 +149,5 @@
|
|
|
149
149
|
"typescript": "^4.3.5",
|
|
150
150
|
"update-dotenv": "^1.1.1"
|
|
151
151
|
},
|
|
152
|
-
"gitHead": "
|
|
152
|
+
"gitHead": "65077556b1a5e4bef39d3b6b2631ab9094cdc65f"
|
|
153
153
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { streamBackup } = require("../../utilities/fileSystem")
|
|
2
2
|
|
|
3
3
|
exports.exportAppDump = async function (ctx) {
|
|
4
4
|
const { appId } = ctx.query
|
|
5
|
-
const
|
|
6
|
-
const backupIdentifier = `${
|
|
7
|
-
|
|
5
|
+
const appName = decodeURI(ctx.query.appname)
|
|
6
|
+
const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt`
|
|
8
7
|
ctx.attachment(backupIdentifier)
|
|
9
|
-
ctx.body = await
|
|
8
|
+
ctx.body = await streamBackup(appId)
|
|
10
9
|
}
|
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
breakRowIdField,
|
|
17
17
|
generateRowIdField,
|
|
18
|
+
isRowId,
|
|
19
|
+
convertRowId,
|
|
18
20
|
} from "../../../integrations/utils"
|
|
19
|
-
import { RelationshipTypes } from "../../../constants"
|
|
20
21
|
|
|
21
22
|
interface ManyRelationship {
|
|
22
23
|
tableId?: string
|
|
@@ -36,7 +37,7 @@ interface RunConfig {
|
|
|
36
37
|
|
|
37
38
|
module External {
|
|
38
39
|
const { makeExternalQuery } = require("./utils")
|
|
39
|
-
const { DataSourceOperation, FieldTypes } = require("../../../constants")
|
|
40
|
+
const { DataSourceOperation, FieldTypes, RelationshipTypes } = require("../../../constants")
|
|
40
41
|
const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
|
|
41
42
|
const { processObjectSync } = require("@budibase/string-templates")
|
|
42
43
|
const { cloneDeep } = require("lodash/fp")
|
|
@@ -83,6 +84,48 @@ module External {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
/**
|
|
88
|
+
* This function checks the incoming parameters to make sure all the inputs are
|
|
89
|
+
* valid based on on the table schema. The main thing this is looking for is when a
|
|
90
|
+
* user has made use of the _id field of a row for a foreign key or a search parameter.
|
|
91
|
+
* In these cases the key will be sent up as [1], rather than 1. In these cases we will
|
|
92
|
+
* simplify it down to the requirements. This function is quite complex as we try to be
|
|
93
|
+
* relatively restrictive over what types of columns we will perform this action for.
|
|
94
|
+
*/
|
|
95
|
+
function cleanupConfig(config: RunConfig, table: Table): RunConfig {
|
|
96
|
+
const primaryOptions = [
|
|
97
|
+
FieldTypes.STRING,
|
|
98
|
+
FieldTypes.LONGFORM,
|
|
99
|
+
FieldTypes.OPTIONS,
|
|
100
|
+
FieldTypes.NUMBER,
|
|
101
|
+
]
|
|
102
|
+
// filter out fields which cannot be keys
|
|
103
|
+
const fieldNames = Object.entries(table.schema)
|
|
104
|
+
.filter(schema => primaryOptions.find(val => val === schema[1].type))
|
|
105
|
+
.map(([fieldName]) => fieldName)
|
|
106
|
+
const iterateObject = (obj: { [key: string]: any }) => {
|
|
107
|
+
for (let [field, value] of Object.entries(obj)) {
|
|
108
|
+
if (fieldNames.find(name => name === field) && isRowId(value)) {
|
|
109
|
+
obj[field] = convertRowId(value)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// check the row and filters to make sure they aren't a key of some sort
|
|
114
|
+
if (config.filters) {
|
|
115
|
+
for (let filter of Object.values(config.filters)) {
|
|
116
|
+
if (typeof filter !== "object" || Object.keys(filter).length === 0) {
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
iterateObject(filter)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (config.row) {
|
|
123
|
+
iterateObject(config.row)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return config
|
|
127
|
+
}
|
|
128
|
+
|
|
86
129
|
function generateIdForRow(row: Row | undefined, table: Table): string {
|
|
87
130
|
const primary = table.primary
|
|
88
131
|
if (!row || !primary) {
|
|
@@ -509,7 +552,7 @@ module External {
|
|
|
509
552
|
return fields
|
|
510
553
|
}
|
|
511
554
|
|
|
512
|
-
async run(
|
|
555
|
+
async run(config: RunConfig) {
|
|
513
556
|
const { appId, operation, tableId } = this
|
|
514
557
|
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
515
558
|
if (!this.datasource) {
|
|
@@ -525,9 +568,11 @@ module External {
|
|
|
525
568
|
if (!table) {
|
|
526
569
|
throw `Unable to process query, table "${tableName}" not defined.`
|
|
527
570
|
}
|
|
528
|
-
//
|
|
571
|
+
// look for specific components of config which may not be considered acceptable
|
|
572
|
+
let { id, row, filters, sort, paginate } = cleanupConfig(config, table)
|
|
529
573
|
filters = buildFilters(id, filters || {}, table)
|
|
530
574
|
const relationships = this.buildRelationships(table)
|
|
575
|
+
// clean up row on ingress using schema
|
|
531
576
|
const processed = this.inputProcessing(row, table)
|
|
532
577
|
row = processed.row
|
|
533
578
|
if (
|
|
@@ -5,6 +5,7 @@ const { DocumentTypes, SEPARATOR } = require("../db/utils")
|
|
|
5
5
|
const { FieldTypes } = require("../constants")
|
|
6
6
|
|
|
7
7
|
const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
|
|
8
|
+
const ROW_ID_REGEX = /^\[.*]$/g
|
|
8
9
|
|
|
9
10
|
export function isExternalTable(tableId: string) {
|
|
10
11
|
return tableId.includes(DocumentTypes.DATASOURCE)
|
|
@@ -32,6 +33,20 @@ export function generateRowIdField(keyProps: any[] = []) {
|
|
|
32
33
|
return encodeURIComponent(JSON.stringify(keyProps).replace(/"/g, "'"))
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
export function isRowId(field: any) {
|
|
37
|
+
return Array.isArray(field) || (typeof field === "string" && field.match(ROW_ID_REGEX) != null)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function convertRowId(field: any) {
|
|
41
|
+
if (Array.isArray(field)) {
|
|
42
|
+
return field[0]
|
|
43
|
+
}
|
|
44
|
+
if (typeof field === "string" && field.match(ROW_ID_REGEX) != null) {
|
|
45
|
+
return field.substring(1, field.length - 1)
|
|
46
|
+
}
|
|
47
|
+
return field
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
// should always return an array
|
|
36
51
|
export function breakRowIdField(_id: string | { _id: string }): any[] {
|
|
37
52
|
if (!_id) {
|
|
@@ -106,64 +106,86 @@ exports.apiFileReturn = contents => {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* @param {string}
|
|
112
|
-
* @
|
|
113
|
-
*
|
|
109
|
+
* Local utility to back up the database state for an app, excluding global user
|
|
110
|
+
* data or user relationships.
|
|
111
|
+
* @param {string} appId The app to backup
|
|
112
|
+
* @param {object} config Config to send to export DB
|
|
113
|
+
* @returns {*} either a string or a stream of the backup
|
|
114
114
|
*/
|
|
115
|
-
|
|
116
|
-
return exports.exportDB(appId, {
|
|
117
|
-
|
|
115
|
+
const backupAppData = async (appId, config) => {
|
|
116
|
+
return await exports.exportDB(appId, {
|
|
117
|
+
...config,
|
|
118
118
|
filter: doc =>
|
|
119
119
|
!(
|
|
120
120
|
doc._id.includes(USER_METDATA_PREFIX) ||
|
|
121
|
-
doc.includes(LINK_USER_METADATA_PREFIX)
|
|
121
|
+
doc._id.includes(LINK_USER_METADATA_PREFIX)
|
|
122
122
|
),
|
|
123
123
|
})
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
*
|
|
127
|
+
* Takes a copy of the database state for an app to the object store.
|
|
128
|
+
* @param {string} appId The ID of the app which is to be backed up.
|
|
129
|
+
* @param {string} backupName The name of the backup located in the object store.
|
|
130
|
+
* @return {*} a readable stream to the completed backup file
|
|
131
|
+
*/
|
|
132
|
+
exports.performBackup = async (appId, backupName) => {
|
|
133
|
+
return await backupAppData(appId, { exportName: backupName })
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Streams a backup of the database state for an app
|
|
138
|
+
* @param {string} appId The ID of the app which is to be backed up.
|
|
139
|
+
* @returns {*} a readable stream of the backup which is written in real time
|
|
140
|
+
*/
|
|
141
|
+
exports.streamBackup = async appId => {
|
|
142
|
+
return await backupAppData(appId, { stream: true })
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Exports a DB to either file or a variable (memory).
|
|
128
147
|
* @param {string} dbName the DB which is to be exported.
|
|
129
|
-
* @param {string} exportName optional -
|
|
148
|
+
* @param {string} exportName optional - provide a filename to write the backup to a file
|
|
149
|
+
* @param {boolean} stream optional - whether to perform a full backup
|
|
130
150
|
* @param {function} filter optional - a filter function to clear out any un-wanted docs.
|
|
131
|
-
* @return
|
|
151
|
+
* @return {*} either a readable stream or a string
|
|
132
152
|
*/
|
|
133
|
-
exports.exportDB = async (
|
|
134
|
-
dbName,
|
|
135
|
-
{ exportName, filter } = { exportName: undefined, filter: undefined }
|
|
136
|
-
) => {
|
|
137
|
-
let stream,
|
|
138
|
-
appString = "",
|
|
139
|
-
path = null
|
|
140
|
-
if (exportName) {
|
|
141
|
-
path = join(budibaseTempDir(), exportName)
|
|
142
|
-
stream = fs.createWriteStream(path)
|
|
143
|
-
} else {
|
|
144
|
-
stream = new MemoryStream()
|
|
145
|
-
stream.on("data", chunk => {
|
|
146
|
-
appString += chunk.toString()
|
|
147
|
-
})
|
|
148
|
-
}
|
|
149
|
-
// perform couch dump
|
|
153
|
+
exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => {
|
|
150
154
|
const instanceDb = new CouchDB(dbName)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return
|
|
155
|
+
|
|
156
|
+
// Stream the dump if required
|
|
157
|
+
if (stream) {
|
|
158
|
+
const memStream = new MemoryStream()
|
|
159
|
+
instanceDb.dump(memStream, { filter })
|
|
160
|
+
return memStream
|
|
157
161
|
}
|
|
158
|
-
|
|
159
|
-
if
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
|
|
163
|
+
// Write the dump to file if required
|
|
164
|
+
if (exportName) {
|
|
165
|
+
const path = join(budibaseTempDir(), exportName)
|
|
166
|
+
const writeStream = fs.createWriteStream(path)
|
|
167
|
+
await instanceDb.dump(writeStream, { filter })
|
|
168
|
+
|
|
169
|
+
// Upload the dump to the object store if self hosted
|
|
170
|
+
if (env.SELF_HOSTED) {
|
|
171
|
+
await streamUpload(
|
|
172
|
+
ObjectStoreBuckets.BACKUPS,
|
|
173
|
+
join(dbName, exportName),
|
|
174
|
+
fs.createReadStream(path)
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return fs.createReadStream(path)
|
|
165
179
|
}
|
|
166
|
-
|
|
180
|
+
|
|
181
|
+
// Stringify the dump in memory if required
|
|
182
|
+
const memStream = new MemoryStream()
|
|
183
|
+
let appString = ""
|
|
184
|
+
memStream.on("data", chunk => {
|
|
185
|
+
appString += chunk.toString()
|
|
186
|
+
})
|
|
187
|
+
await instanceDb.dump(memStream, { filter })
|
|
188
|
+
return appString
|
|
167
189
|
}
|
|
168
190
|
|
|
169
191
|
/**
|