@budibase/server 0.9.169-alpha.9 → 0.9.172
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.aafb155b.js → index.9ab01cd4.js} +34 -34
- package/builder/assets/{index.0ea76a5c.css → index.dbc6728e.css} +2 -2
- package/builder/assets/{vendor.2053eff2.js → vendor.54435e8f.js} +2 -2
- package/builder/index.html +3 -3
- package/coverage/clover.xml +517 -609
- package/coverage/coverage-final.json +31 -32
- 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 +9 -6
- 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 +17 -41
- package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +45 -45
- 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 +4 -7
- package/coverage/lcov-report/api/controllers/query.js.html +24 -129
- 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 +15 -15
- 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 +57 -12
- package/coverage/lcov-report/api/controllers/table/index.html +28 -28
- package/coverage/lcov-report/api/controllers/table/index.js.html +37 -16
- package/coverage/lcov-report/api/controllers/table/utils.js.html +33 -279
- package/coverage/lcov-report/api/controllers/templates.js.html +13 -49
- 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 +8 -8
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +6 -6
- 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 +11 -26
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +9 -9
- 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 +3 -9
- 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 +10 -31
- package/coverage/lcov-report/automations/automationUtils.js.html +86 -14
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- package/coverage/lcov-report/automations/index.html +25 -25
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +7 -10
- package/coverage/lcov-report/automations/steps/createRow.js.html +2 -2
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +5 -8
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +7 -10
- package/coverage/lcov-report/automations/steps/executeScript.js.html +7 -10
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +57 -57
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +7 -10
- package/coverage/lcov-report/automations/steps/queryRows.js.html +7 -10
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +7 -10
- 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 +14 -14
- package/coverage/lcov-report/automations/steps/utils.js.html +10 -10
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/thread.js.html +12 -54
- 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 +2 -5
- 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 +12 -12
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +9 -9
- 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 +69 -69
- 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 +2 -2
- 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 +2 -2
- package/coverage/lcov-report/utilities/index.html +7 -22
- 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 +14 -14
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +8 -38
- package/coverage/lcov-report/utilities/rowProcessor/utils.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 +848 -1026
- package/dist/api/controllers/backup.js +4 -4
- package/dist/api/controllers/dev.js +11 -17
- package/dist/api/controllers/permission.js +0 -1
- package/dist/api/controllers/query.js +8 -26
- package/dist/api/controllers/row/ExternalRequest.js +5 -46
- package/dist/api/controllers/script.js +16 -4
- package/dist/api/controllers/table/index.js +10 -5
- package/dist/api/controllers/table/utils.js +10 -83
- package/dist/api/controllers/templates.js +3 -18
- package/dist/api/routes/dev.js +4 -8
- package/dist/api/routes/query.js +1 -3
- package/dist/automations/actions.js +3 -9
- package/dist/automations/automationUtils.js +29 -8
- package/dist/automations/steps/bash.js +1 -2
- package/dist/automations/steps/createRow.js +1 -1
- package/dist/automations/steps/deleteRow.js +1 -2
- package/dist/automations/steps/executeQuery.js +1 -2
- package/dist/automations/steps/executeScript.js +1 -2
- package/dist/automations/steps/outgoingWebhook.js +1 -2
- package/dist/automations/steps/queryRows.js +1 -2
- package/dist/automations/steps/sendSmtpEmail.js +1 -2
- package/dist/automations/steps/updateRow.js +2 -6
- package/dist/automations/thread.js +3 -14
- package/dist/constants/index.js +0 -1
- package/dist/integrations/base/sql.js +4 -4
- package/dist/integrations/utils.js +1 -16
- package/dist/middleware/currentapp.js +1 -1
- package/dist/package.json +5 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/index.js +33 -49
- package/dist/utilities/global.js +1 -1
- package/dist/utilities/rowProcessor/index.js +0 -8
- package/package.json +6 -7
- package/src/api/controllers/backup.js +5 -4
- package/src/api/controllers/dev.js +8 -16
- package/src/api/controllers/permission.js +0 -1
- package/src/api/controllers/query.js +11 -46
- package/src/api/controllers/row/ExternalRequest.ts +4 -49
- package/src/api/controllers/script.js +19 -4
- package/src/api/controllers/table/index.js +12 -5
- package/src/api/controllers/table/utils.js +12 -94
- package/src/api/controllers/templates.js +5 -17
- package/src/api/routes/dev.js +4 -9
- package/src/api/routes/query.js +1 -3
- package/src/automations/actions.js +3 -10
- package/src/automations/automationUtils.js +31 -7
- package/src/automations/steps/bash.js +1 -2
- package/src/automations/steps/createRow.js +1 -1
- package/src/automations/steps/deleteRow.js +1 -2
- package/src/automations/steps/executeQuery.js +1 -2
- package/src/automations/steps/executeScript.js +1 -2
- package/src/automations/steps/outgoingWebhook.js +1 -2
- package/src/automations/steps/queryRows.js +1 -2
- package/src/automations/steps/sendSmtpEmail.js +1 -2
- package/src/automations/steps/updateRow.js +6 -6
- package/src/automations/thread.js +3 -17
- package/src/constants/index.js +0 -1
- package/src/integrations/base/sql.ts +3 -5
- package/src/integrations/tests/mongo.spec.js +6 -10
- package/src/integrations/utils.ts +0 -15
- package/src/middleware/currentapp.js +1 -1
- package/src/middleware/tests/currentapp.spec.js +3 -3
- package/src/utilities/fileSystem/index.js +43 -65
- package/src/utilities/global.js +1 -1
- package/src/utilities/rowProcessor/index.js +0 -10
- package/coverage/lcov-report/utilities/scriptRunner.js.html +0 -143
- package/dist/utilities/scriptRunner.js +0 -19
- package/src/utilities/scriptRunner.js +0 -21
|
@@ -98,69 +98,53 @@ 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
|
-
});
|
|
112
101
|
/**
|
|
113
102
|
* Takes a copy of the database state for an app to the object store.
|
|
114
103
|
* @param {string} appId The ID of the app which is to be backed up.
|
|
115
104
|
* @param {string} backupName The name of the backup located in the object store.
|
|
116
|
-
* @return
|
|
105
|
+
* @return The backup has been completed when this promise completes and returns a file stream
|
|
106
|
+
* to the temporary backup file (to return via API if required).
|
|
117
107
|
*/
|
|
118
108
|
exports.performBackup = (appId, backupName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
119
|
-
return
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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 });
|
|
109
|
+
return exports.exportDB(appId, {
|
|
110
|
+
exportName: backupName,
|
|
111
|
+
filter: doc => !(doc._id.includes(USER_METDATA_PREFIX) ||
|
|
112
|
+
doc.includes(LINK_USER_METADATA_PREFIX)),
|
|
113
|
+
});
|
|
128
114
|
});
|
|
129
115
|
/**
|
|
130
|
-
*
|
|
116
|
+
* exports a DB to either file or a variable (memory).
|
|
131
117
|
* @param {string} dbName the DB which is to be exported.
|
|
132
|
-
* @param {string} exportName optional -
|
|
133
|
-
* @param {boolean} stream optional - whether to perform a full backup
|
|
118
|
+
* @param {string} exportName optional - the file name to export to, if not in memory.
|
|
134
119
|
* @param {function} filter optional - a filter function to clear out any un-wanted docs.
|
|
135
|
-
* @return
|
|
120
|
+
* @return Either the file stream or the variable (if no export name provided).
|
|
136
121
|
*/
|
|
137
|
-
exports.exportDB = (dbName, {
|
|
138
|
-
|
|
139
|
-
// Stream the dump if required
|
|
140
|
-
if (stream) {
|
|
141
|
-
const memStream = new MemoryStream();
|
|
142
|
-
instanceDb.dump(memStream, { filter });
|
|
143
|
-
return memStream;
|
|
144
|
-
}
|
|
145
|
-
// Write the dump to file if required
|
|
122
|
+
exports.exportDB = (dbName, { exportName, filter } = { exportName: undefined, filter: undefined }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
let stream, appString = "", path = null;
|
|
146
124
|
if (exportName) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
+
});
|
|
155
133
|
}
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
appString += chunk.toString();
|
|
134
|
+
// perform couch dump
|
|
135
|
+
const instanceDb = new CouchDB(dbName);
|
|
136
|
+
yield instanceDb.dump(stream, {
|
|
137
|
+
filter,
|
|
161
138
|
});
|
|
162
|
-
|
|
163
|
-
|
|
139
|
+
// just in memory, return the final string
|
|
140
|
+
if (!exportName) {
|
|
141
|
+
return appString;
|
|
142
|
+
}
|
|
143
|
+
// write the file to the object store
|
|
144
|
+
if (env.SELF_HOSTED) {
|
|
145
|
+
yield streamUpload(ObjectStoreBuckets.BACKUPS, join(dbName, exportName), fs.createReadStream(path));
|
|
146
|
+
}
|
|
147
|
+
return fs.createReadStream(path);
|
|
164
148
|
});
|
|
165
149
|
/**
|
|
166
150
|
* Writes the provided contents to a temporary file, which can be used briefly.
|
package/dist/utilities/global.js
CHANGED
|
@@ -149,10 +149,6 @@ exports.processAutoColumn = processAutoColumn;
|
|
|
149
149
|
* @returns {object} The coerced value
|
|
150
150
|
*/
|
|
151
151
|
exports.coerce = (row, type) => {
|
|
152
|
-
// no coercion specified for type, skip it
|
|
153
|
-
if (!TYPE_TRANSFORM_MAP[type]) {
|
|
154
|
-
return row;
|
|
155
|
-
}
|
|
156
152
|
// eslint-disable-next-line no-prototype-builtins
|
|
157
153
|
if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
|
|
158
154
|
return TYPE_TRANSFORM_MAP[type][row];
|
|
@@ -194,10 +190,6 @@ exports.inputProcessing = (user = {}, table, row, opts = { noAutoRelationships:
|
|
|
194
190
|
clonedRow[key] = exports.coerce(value, field.type);
|
|
195
191
|
}
|
|
196
192
|
}
|
|
197
|
-
if (!clonedRow._id || !clonedRow._rev) {
|
|
198
|
-
clonedRow._id = row._id;
|
|
199
|
-
clonedRow._rev = row._rev;
|
|
200
|
-
}
|
|
201
193
|
// handle auto columns - this returns an object like {table, row}
|
|
202
194
|
return processAutoColumn(user, copiedTable, clonedRow, opts);
|
|
203
195
|
};
|
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.
|
|
4
|
+
"version": "0.9.172",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"repository": {
|
|
@@ -68,13 +68,13 @@
|
|
|
68
68
|
"author": "Budibase",
|
|
69
69
|
"license": "AGPL-3.0-or-later",
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@budibase/auth": "^0.9.
|
|
72
|
-
"@budibase/client": "^0.9.
|
|
73
|
-
"@budibase/string-templates": "^0.9.
|
|
71
|
+
"@budibase/auth": "^0.9.172",
|
|
72
|
+
"@budibase/client": "^0.9.172",
|
|
73
|
+
"@budibase/string-templates": "^0.9.172",
|
|
74
74
|
"@elastic/elasticsearch": "7.10.0",
|
|
75
75
|
"@koa/router": "8.0.0",
|
|
76
76
|
"@sendgrid/mail": "7.1.1",
|
|
77
|
-
"@sentry/node": "
|
|
77
|
+
"@sentry/node": "5.19.2",
|
|
78
78
|
"airtable": "0.10.1",
|
|
79
79
|
"arangojs": "7.2.0",
|
|
80
80
|
"aws-sdk": "^2.767.0",
|
|
@@ -119,7 +119,6 @@
|
|
|
119
119
|
"to-json-schema": "0.2.5",
|
|
120
120
|
"uuid": "3.3.2",
|
|
121
121
|
"validate.js": "0.13.1",
|
|
122
|
-
"vm2": "^3.9.3",
|
|
123
122
|
"yargs": "13.2.4",
|
|
124
123
|
"zlib": "1.0.5"
|
|
125
124
|
},
|
|
@@ -149,5 +148,5 @@
|
|
|
149
148
|
"typescript": "^4.3.5",
|
|
150
149
|
"update-dotenv": "^1.1.1"
|
|
151
150
|
},
|
|
152
|
-
"gitHead": "
|
|
151
|
+
"gitHead": "f33cc5a9cd747ffb8491af36ac7baf0bf66de290"
|
|
153
152
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { performBackup } = require("../../utilities/fileSystem")
|
|
2
2
|
|
|
3
3
|
exports.exportAppDump = async function (ctx) {
|
|
4
4
|
const { appId } = ctx.query
|
|
5
|
-
const
|
|
6
|
-
const backupIdentifier = `${
|
|
5
|
+
const appname = decodeURI(ctx.query.appname)
|
|
6
|
+
const backupIdentifier = `${appname}Backup${new Date().getTime()}.txt`
|
|
7
|
+
|
|
7
8
|
ctx.attachment(backupIdentifier)
|
|
8
|
-
ctx.body = await
|
|
9
|
+
ctx.body = await performBackup(appId, backupIdentifier)
|
|
9
10
|
}
|
|
@@ -7,13 +7,11 @@ const { clearLock } = require("../../utilities/redis")
|
|
|
7
7
|
const { Replication } = require("@budibase/auth").db
|
|
8
8
|
const { DocumentTypes } = require("../../db/utils")
|
|
9
9
|
|
|
10
|
-
async function redirect(ctx, method
|
|
10
|
+
async function redirect(ctx, method) {
|
|
11
11
|
const { devPath } = ctx.params
|
|
12
12
|
const queryString = ctx.originalUrl.split("?")[1] || ""
|
|
13
13
|
const response = await fetch(
|
|
14
|
-
checkSlashesInUrl(
|
|
15
|
-
`${env.WORKER_URL}/api/${path}/${devPath}?${queryString}`
|
|
16
|
-
),
|
|
14
|
+
checkSlashesInUrl(`${env.WORKER_URL}/api/global/${devPath}?${queryString}`),
|
|
17
15
|
request(
|
|
18
16
|
ctx,
|
|
19
17
|
{
|
|
@@ -43,22 +41,16 @@ async function redirect(ctx, method, path = "global") {
|
|
|
43
41
|
ctx.cookies
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
exports.
|
|
47
|
-
|
|
48
|
-
await redirect(ctx, "GET", path)
|
|
49
|
-
}
|
|
44
|
+
exports.redirectGet = async ctx => {
|
|
45
|
+
await redirect(ctx, "GET")
|
|
50
46
|
}
|
|
51
47
|
|
|
52
|
-
exports.
|
|
53
|
-
|
|
54
|
-
await redirect(ctx, "POST", path)
|
|
55
|
-
}
|
|
48
|
+
exports.redirectPost = async ctx => {
|
|
49
|
+
await redirect(ctx, "POST")
|
|
56
50
|
}
|
|
57
51
|
|
|
58
|
-
exports.
|
|
59
|
-
|
|
60
|
-
await redirect(ctx, "DELETE", path)
|
|
61
|
-
}
|
|
52
|
+
exports.redirectDelete = async ctx => {
|
|
53
|
+
await redirect(ctx, "DELETE")
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
exports.clearLock = async ctx => {
|
|
@@ -4,7 +4,6 @@ const { generateQueryID, getQueryParams } = require("../../db/utils")
|
|
|
4
4
|
const { integrations } = require("../../integrations")
|
|
5
5
|
const { BaseQueryVerbs } = require("../../constants")
|
|
6
6
|
const env = require("../../environment")
|
|
7
|
-
const ScriptRunner = require("../../utilities/scriptRunner")
|
|
8
7
|
|
|
9
8
|
// simple function to append "readable" to all read queries
|
|
10
9
|
function enrichQueries(input) {
|
|
@@ -29,37 +28,10 @@ function formatResponse(resp) {
|
|
|
29
28
|
resp = { response: resp }
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
async function runAndTransform(
|
|
36
|
-
integration,
|
|
37
|
-
queryVerb,
|
|
38
|
-
enrichedQuery,
|
|
39
|
-
transformer
|
|
40
|
-
) {
|
|
41
|
-
let rows = formatResponse(await integration[queryVerb](enrichedQuery))
|
|
42
|
-
|
|
43
|
-
// transform as required
|
|
44
|
-
if (transformer) {
|
|
45
|
-
const runner = new ScriptRunner(transformer, { data: rows })
|
|
46
|
-
rows = runner.execute()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// needs to an array for next step
|
|
50
|
-
if (!Array.isArray(rows)) {
|
|
51
|
-
rows = [rows]
|
|
31
|
+
if (!Array.isArray(resp)) {
|
|
32
|
+
resp = [resp]
|
|
52
33
|
}
|
|
53
|
-
|
|
54
|
-
// map into JSON if just raw primitive here
|
|
55
|
-
if (rows.find(row => typeof row !== "object")) {
|
|
56
|
-
rows = rows.map(value => ({ value }))
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// get all the potential fields in the schema
|
|
60
|
-
let keys = rows.flatMap(Object.keys)
|
|
61
|
-
|
|
62
|
-
return { rows, keys }
|
|
34
|
+
return resp
|
|
63
35
|
}
|
|
64
36
|
|
|
65
37
|
exports.fetch = async function (ctx) {
|
|
@@ -150,16 +122,15 @@ exports.preview = async function (ctx) {
|
|
|
150
122
|
ctx.throw(400, "Integration type does not exist.")
|
|
151
123
|
}
|
|
152
124
|
|
|
153
|
-
const { fields, parameters, queryVerb
|
|
125
|
+
const { fields, parameters, queryVerb } = ctx.request.body
|
|
126
|
+
|
|
154
127
|
const enrichedQuery = await enrichQueryFields(fields, parameters)
|
|
128
|
+
|
|
155
129
|
const integration = new Integration(datasource.config)
|
|
130
|
+
const rows = formatResponse(await integration[queryVerb](enrichedQuery))
|
|
156
131
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
queryVerb,
|
|
160
|
-
enrichedQuery,
|
|
161
|
-
transformer
|
|
162
|
-
)
|
|
132
|
+
// get all the potential fields in the schema
|
|
133
|
+
const keys = rows.flatMap(Object.keys)
|
|
163
134
|
|
|
164
135
|
ctx.body = {
|
|
165
136
|
rows,
|
|
@@ -187,16 +158,10 @@ exports.execute = async function (ctx) {
|
|
|
187
158
|
query.fields,
|
|
188
159
|
ctx.request.body.parameters
|
|
189
160
|
)
|
|
190
|
-
const integration = new Integration(datasource.config)
|
|
191
161
|
|
|
162
|
+
const integration = new Integration(datasource.config)
|
|
192
163
|
// call the relevant CRUD method on the integration class
|
|
193
|
-
|
|
194
|
-
integration,
|
|
195
|
-
query.queryVerb,
|
|
196
|
-
enrichedQuery,
|
|
197
|
-
query.transformer
|
|
198
|
-
)
|
|
199
|
-
ctx.body = rows
|
|
164
|
+
ctx.body = formatResponse(await integration[query.queryVerb](enrichedQuery))
|
|
200
165
|
// cleanup
|
|
201
166
|
if (integration.end) {
|
|
202
167
|
integration.end()
|
|
@@ -15,9 +15,8 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
breakRowIdField,
|
|
17
17
|
generateRowIdField,
|
|
18
|
-
isRowId,
|
|
19
|
-
convertRowId,
|
|
20
18
|
} from "../../../integrations/utils"
|
|
19
|
+
import { RelationshipTypes } from "../../../constants"
|
|
21
20
|
|
|
22
21
|
interface ManyRelationship {
|
|
23
22
|
tableId?: string
|
|
@@ -37,7 +36,7 @@ interface RunConfig {
|
|
|
37
36
|
|
|
38
37
|
module External {
|
|
39
38
|
const { makeExternalQuery } = require("./utils")
|
|
40
|
-
const { DataSourceOperation, FieldTypes
|
|
39
|
+
const { DataSourceOperation, FieldTypes } = require("../../../constants")
|
|
41
40
|
const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
|
|
42
41
|
const { processObjectSync } = require("@budibase/string-templates")
|
|
43
42
|
const { cloneDeep } = require("lodash/fp")
|
|
@@ -84,48 +83,6 @@ module External {
|
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
|
|
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
|
-
|
|
129
86
|
function generateIdForRow(row: Row | undefined, table: Table): string {
|
|
130
87
|
const primary = table.primary
|
|
131
88
|
if (!row || !primary) {
|
|
@@ -552,7 +509,7 @@ module External {
|
|
|
552
509
|
return fields
|
|
553
510
|
}
|
|
554
511
|
|
|
555
|
-
async run(
|
|
512
|
+
async run({ id, row, filters, sort, paginate }: RunConfig) {
|
|
556
513
|
const { appId, operation, tableId } = this
|
|
557
514
|
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
558
515
|
if (!this.datasource) {
|
|
@@ -568,11 +525,9 @@ module External {
|
|
|
568
525
|
if (!table) {
|
|
569
526
|
throw `Unable to process query, table "${tableName}" not defined.`
|
|
570
527
|
}
|
|
571
|
-
//
|
|
572
|
-
let { id, row, filters, sort, paginate } = cleanupConfig(config, table)
|
|
528
|
+
// clean up row on ingress using schema
|
|
573
529
|
filters = buildFilters(id, filters || {}, table)
|
|
574
530
|
const relationships = this.buildRelationships(table)
|
|
575
|
-
// clean up row on ingress using schema
|
|
576
531
|
const processed = this.inputProcessing(row, table)
|
|
577
532
|
row = processed.row
|
|
578
533
|
if (
|
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
const
|
|
1
|
+
const fetch = require("node-fetch")
|
|
2
|
+
const vm = require("vm")
|
|
3
|
+
|
|
4
|
+
class ScriptExecutor {
|
|
5
|
+
constructor(body) {
|
|
6
|
+
const code = `let fn = () => {\n${body.script}\n}; out = fn();`
|
|
7
|
+
this.script = new vm.Script(code)
|
|
8
|
+
this.context = vm.createContext(body.context)
|
|
9
|
+
this.context.fetch = fetch
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
execute() {
|
|
13
|
+
this.script.runInContext(this.context)
|
|
14
|
+
return this.context.out
|
|
15
|
+
}
|
|
16
|
+
}
|
|
2
17
|
|
|
3
18
|
exports.execute = async function (ctx) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
ctx.body =
|
|
19
|
+
const executor = new ScriptExecutor(ctx.request.body)
|
|
20
|
+
|
|
21
|
+
ctx.body = executor.execute()
|
|
7
22
|
}
|
|
8
23
|
|
|
9
24
|
exports.save = async function (ctx) {
|
|
@@ -9,7 +9,11 @@ const {
|
|
|
9
9
|
BudibaseInternalDB,
|
|
10
10
|
} = require("../../../db/utils")
|
|
11
11
|
const { FieldTypes } = require("../../../constants")
|
|
12
|
-
const { TableSaveFunctions,
|
|
12
|
+
const { TableSaveFunctions, getExternalTable } = require("./utils")
|
|
13
|
+
const {
|
|
14
|
+
isExternalTable,
|
|
15
|
+
breakExternalTableId,
|
|
16
|
+
} = require("../../../integrations/utils")
|
|
13
17
|
|
|
14
18
|
exports.fetch = async function (ctx) {
|
|
15
19
|
const db = new CouchDB(ctx.appId)
|
|
@@ -44,8 +48,14 @@ exports.fetch = async function (ctx) {
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
exports.find = async function (ctx) {
|
|
51
|
+
const db = new CouchDB(ctx.appId)
|
|
47
52
|
const tableId = ctx.params.id
|
|
48
|
-
|
|
53
|
+
if (isExternalTable(tableId)) {
|
|
54
|
+
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
55
|
+
ctx.body = await getExternalTable(ctx.appId, datasourceId, tableName)
|
|
56
|
+
} else {
|
|
57
|
+
ctx.body = await db.get(ctx.params.id)
|
|
58
|
+
}
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
exports.save = async function (ctx) {
|
|
@@ -81,9 +91,6 @@ exports.save = async function (ctx) {
|
|
|
81
91
|
for (let propKey of Object.keys(tableToSave.schema)) {
|
|
82
92
|
let column = tableToSave.schema[propKey]
|
|
83
93
|
let oldColumn = oldTable.schema[propKey]
|
|
84
|
-
if (oldColumn && oldColumn.type === "internal") {
|
|
85
|
-
oldColumn.type = "auto"
|
|
86
|
-
}
|
|
87
94
|
if (oldColumn && oldColumn.type !== column.type) {
|
|
88
95
|
ctx.throw(400, "Cannot change the type of a column")
|
|
89
96
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const CouchDB = require("../../../db")
|
|
2
|
+
const linkRows = require("../../../db/linkedRows")
|
|
2
3
|
const csvParser = require("../../../utilities/csvParser")
|
|
3
4
|
const {
|
|
4
5
|
getRowParams,
|
|
@@ -9,12 +10,6 @@ const { isEqual } = require("lodash/fp")
|
|
|
9
10
|
const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
|
|
10
11
|
const { inputProcessing } = require("../../../utilities/rowProcessor")
|
|
11
12
|
const { USERS_TABLE_SCHEMA } = require("../../../constants")
|
|
12
|
-
const {
|
|
13
|
-
isExternalTable,
|
|
14
|
-
breakExternalTableId,
|
|
15
|
-
} = require("../../../integrations/utils")
|
|
16
|
-
const { getViews, saveView } = require("../view/utils")
|
|
17
|
-
const viewTemplate = require("../view/viewBuilder")
|
|
18
13
|
|
|
19
14
|
exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
|
20
15
|
let updatedRows = []
|
|
@@ -27,7 +22,6 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
|
|
27
22
|
}
|
|
28
23
|
// check for renaming of columns or deleted columns
|
|
29
24
|
if (rename || deletedColumns.length !== 0) {
|
|
30
|
-
// Update all rows
|
|
31
25
|
const rows = await db.allDocs(
|
|
32
26
|
getRowParams(updatedTable._id, null, {
|
|
33
27
|
include_docs: true,
|
|
@@ -42,9 +36,6 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
|
|
42
36
|
}
|
|
43
37
|
return doc
|
|
44
38
|
})
|
|
45
|
-
|
|
46
|
-
// Update views
|
|
47
|
-
await exports.checkForViewUpdates(db, updatedTable, rename, deletedColumns)
|
|
48
39
|
delete updatedTable._rename
|
|
49
40
|
}
|
|
50
41
|
return { rows: updatedRows, table: updatedTable }
|
|
@@ -102,10 +93,19 @@ exports.handleDataImport = async (appId, user, table, dataImport) => {
|
|
|
102
93
|
}
|
|
103
94
|
}
|
|
104
95
|
|
|
105
|
-
|
|
96
|
+
// make sure link rows are up to date
|
|
97
|
+
finalData.push(
|
|
98
|
+
linkRows.updateLinks({
|
|
99
|
+
appId,
|
|
100
|
+
eventType: linkRows.EventType.ROW_SAVE,
|
|
101
|
+
row,
|
|
102
|
+
tableId: row.tableId,
|
|
103
|
+
table,
|
|
104
|
+
})
|
|
105
|
+
)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
await db.bulkDocs(finalData)
|
|
108
|
+
await db.bulkDocs(await Promise.all(finalData))
|
|
109
109
|
let response = await db.put(table)
|
|
110
110
|
table._rev = response._rev
|
|
111
111
|
}
|
|
@@ -233,86 +233,4 @@ exports.getExternalTable = async (appId, datasourceId, tableName) => {
|
|
|
233
233
|
return entities[tableName]
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
exports.getTable = async (appId, tableId) => {
|
|
237
|
-
const db = new CouchDB(appId)
|
|
238
|
-
if (isExternalTable(tableId)) {
|
|
239
|
-
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
240
|
-
return exports.getExternalTable(appId, datasourceId, tableName)
|
|
241
|
-
} else {
|
|
242
|
-
return db.get(tableId)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
|
|
247
|
-
const views = await getViews(db)
|
|
248
|
-
const tableViews = views.filter(view => view.meta.tableId === table._id)
|
|
249
|
-
|
|
250
|
-
// Check each table view to see if impacted by this table action
|
|
251
|
-
for (let view of tableViews) {
|
|
252
|
-
let needsUpdated = false
|
|
253
|
-
|
|
254
|
-
// First check for renames, otherwise check for deletions
|
|
255
|
-
if (rename) {
|
|
256
|
-
// Update calculation field if required
|
|
257
|
-
if (view.meta.field === rename.old) {
|
|
258
|
-
view.meta.field = rename.updated
|
|
259
|
-
needsUpdated = true
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Update group by field if required
|
|
263
|
-
if (view.meta.groupBy === rename.old) {
|
|
264
|
-
view.meta.groupBy = rename.updated
|
|
265
|
-
needsUpdated = true
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Update filters if required
|
|
269
|
-
if (view.meta.filters) {
|
|
270
|
-
view.meta.filters.forEach(filter => {
|
|
271
|
-
if (filter.key === rename.old) {
|
|
272
|
-
filter.key = rename.updated
|
|
273
|
-
needsUpdated = true
|
|
274
|
-
}
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
} else if (deletedColumns) {
|
|
278
|
-
deletedColumns.forEach(column => {
|
|
279
|
-
// Remove calculation statement if required
|
|
280
|
-
if (view.meta.field === column) {
|
|
281
|
-
delete view.meta.field
|
|
282
|
-
delete view.meta.calculation
|
|
283
|
-
delete view.meta.groupBy
|
|
284
|
-
needsUpdated = true
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Remove group by field if required
|
|
288
|
-
if (view.meta.groupBy === column) {
|
|
289
|
-
delete view.meta.groupBy
|
|
290
|
-
needsUpdated = true
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Remove filters referencing deleted field if required
|
|
294
|
-
if (view.meta.filters && view.meta.filters.length) {
|
|
295
|
-
const initialLength = view.meta.filters.length
|
|
296
|
-
view.meta.filters = view.meta.filters.filter(filter => {
|
|
297
|
-
return filter.key !== column
|
|
298
|
-
})
|
|
299
|
-
if (initialLength !== view.meta.filters.length) {
|
|
300
|
-
needsUpdated = true
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
})
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Update view if required
|
|
307
|
-
if (needsUpdated) {
|
|
308
|
-
const newViewTemplate = viewTemplate(view.meta)
|
|
309
|
-
await saveView(db, null, view.name, newViewTemplate)
|
|
310
|
-
if (!newViewTemplate.meta.schema) {
|
|
311
|
-
newViewTemplate.meta.schema = table.schema
|
|
312
|
-
}
|
|
313
|
-
table.views[view.name] = newViewTemplate.meta
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
236
|
exports.TableSaveFunctions = TableSaveFunctions
|
|
@@ -7,23 +7,11 @@ const DEFAULT_TEMPLATES_BUCKET =
|
|
|
7
7
|
|
|
8
8
|
exports.fetch = async function (ctx) {
|
|
9
9
|
const { type = "app" } = ctx.query
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
error = true
|
|
16
|
-
}
|
|
17
|
-
} catch (err) {
|
|
18
|
-
error = true
|
|
19
|
-
}
|
|
20
|
-
// if there is an error, simply return no templates
|
|
21
|
-
if (!error && response) {
|
|
22
|
-
const json = await response.json()
|
|
23
|
-
ctx.body = Object.values(json.templates[type])
|
|
24
|
-
} else {
|
|
25
|
-
ctx.body = []
|
|
26
|
-
}
|
|
10
|
+
const response = await fetch(
|
|
11
|
+
`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`
|
|
12
|
+
)
|
|
13
|
+
const json = await response.json()
|
|
14
|
+
ctx.body = Object.values(json.templates[type])
|
|
27
15
|
}
|
|
28
16
|
|
|
29
17
|
// can't currently test this, have to ignore from coverage
|