@budibase/server 0.9.172 → 0.9.173-alpha.3

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.
Files changed (261) hide show
  1. package/__mocks__/{mysql.ts → mysql2.ts} +0 -0
  2. package/builder/assets/{index.dbc6728e.css → index.7469e1e4.css} +2 -2
  3. package/builder/assets/{index.9ab01cd4.js → index.b38aa4d6.js} +34 -34
  4. package/builder/assets/{vendor.54435e8f.js → vendor.278a305c.js} +1 -1
  5. package/builder/index.html +3 -3
  6. package/coverage/clover.xml +973 -849
  7. package/coverage/coverage-final.json +47 -46
  8. package/coverage/lcov-report/api/controllers/analytics.js.html +21 -9
  9. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/application.js.html +186 -84
  11. package/coverage/lcov-report/api/controllers/auth.js.html +37 -10
  12. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/backup.js.html +6 -9
  14. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/dev.js.html +41 -17
  18. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/index.html +71 -71
  20. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/permission.js.html +7 -4
  24. package/coverage/lcov-report/api/controllers/query.js.html +129 -24
  25. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/index.html +5 -5
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +15 -15
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/screen.js.html +9 -9
  34. package/coverage/lcov-report/api/controllers/script.js.html +12 -57
  35. package/coverage/lcov-report/api/controllers/table/index.html +28 -28
  36. package/coverage/lcov-report/api/controllers/table/index.js.html +16 -37
  37. package/coverage/lcov-report/api/controllers/table/utils.js.html +279 -33
  38. package/coverage/lcov-report/api/controllers/templates.js.html +49 -13
  39. package/coverage/lcov-report/api/controllers/user.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/index.html +19 -19
  42. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/utils.js.html +8 -8
  44. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +54 -15
  45. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  46. package/coverage/lcov-report/api/index.html +1 -1
  47. package/coverage/lcov-report/api/index.js.html +3 -3
  48. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/application.js.html +5 -2
  51. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/dev.js.html +26 -11
  59. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/index.html +9 -9
  61. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/query.js.html +9 -3
  67. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  78. package/coverage/lcov-report/automations/actions.js.html +31 -10
  79. package/coverage/lcov-report/automations/automationUtils.js.html +14 -86
  80. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  81. package/coverage/lcov-report/automations/index.html +37 -37
  82. package/coverage/lcov-report/automations/index.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/bash.js.html +10 -7
  84. package/coverage/lcov-report/automations/steps/createRow.js.html +2 -2
  85. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/deleteRow.js.html +8 -5
  87. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/executeQuery.js.html +10 -7
  89. package/coverage/lcov-report/automations/steps/executeScript.js.html +10 -7
  90. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/index.html +57 -57
  92. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +10 -7
  94. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -7
  95. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +10 -7
  96. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/updateRow.js.html +14 -14
  99. package/coverage/lcov-report/automations/steps/utils.js.html +10 -10
  100. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  101. package/coverage/lcov-report/automations/thread.js.html +54 -12
  102. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  111. package/coverage/lcov-report/automations/utils.js.html +28 -28
  112. package/coverage/lcov-report/constants/index.html +1 -1
  113. package/coverage/lcov-report/constants/index.js.html +5 -2
  114. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  115. package/coverage/lcov-report/constants/screens.js.html +2 -2
  116. package/coverage/lcov-report/db/client.js.html +1 -1
  117. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  118. package/coverage/lcov-report/db/index.html +1 -1
  119. package/coverage/lcov-report/db/index.js.html +1 -1
  120. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +12 -12
  121. package/coverage/lcov-report/db/linkedRows/index.html +18 -18
  122. package/coverage/lcov-report/db/linkedRows/index.js.html +47 -26
  123. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  124. package/coverage/lcov-report/db/newid.js.html +2 -2
  125. package/coverage/lcov-report/db/utils.js.html +17 -17
  126. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  127. package/coverage/lcov-report/events/index.html +1 -1
  128. package/coverage/lcov-report/events/index.js.html +1 -1
  129. package/coverage/lcov-report/events/utils.js.html +1 -1
  130. package/coverage/lcov-report/index.html +107 -107
  131. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  132. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  133. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  134. package/coverage/lcov-report/middleware/authorized.js.html +2 -2
  135. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  136. package/coverage/lcov-report/middleware/currentapp.js.html +52 -25
  137. package/coverage/lcov-report/middleware/index.html +15 -15
  138. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  139. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  140. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  141. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  142. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  143. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  144. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  145. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  146. package/coverage/lcov-report/utilities/global.js.html +27 -27
  147. package/coverage/lcov-report/utilities/index.html +32 -17
  148. package/coverage/lcov-report/utilities/index.js.html +4 -4
  149. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +29 -8
  150. package/coverage/lcov-report/utilities/queue/index.html +15 -15
  151. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  152. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  153. package/coverage/lcov-report/utilities/rowProcessor/index.html +14 -14
  154. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +38 -8
  155. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  156. package/coverage/lcov-report/utilities/scriptRunner.js.html +143 -0
  157. package/coverage/lcov-report/utilities/security.js.html +1 -1
  158. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  159. package/coverage/lcov-report/utilities/users.js.html +1 -1
  160. package/coverage/lcov-report/utilities/workerRequests.js.html +19 -19
  161. package/coverage/lcov.info +1692 -1452
  162. package/dist/api/controllers/analytics.js +4 -0
  163. package/dist/api/controllers/application.js +185 -171
  164. package/dist/api/controllers/auth.js +12 -2
  165. package/dist/api/controllers/backup.js +4 -4
  166. package/dist/api/controllers/deploy/index.js +3 -16
  167. package/dist/api/controllers/dev.js +17 -11
  168. package/dist/api/controllers/permission.js +1 -0
  169. package/dist/api/controllers/query.js +26 -8
  170. package/dist/api/controllers/row/ExternalRequest.js +46 -5
  171. package/dist/api/controllers/script.js +4 -16
  172. package/dist/api/controllers/table/index.js +5 -10
  173. package/dist/api/controllers/table/utils.js +83 -10
  174. package/dist/api/controllers/templates.js +18 -3
  175. package/dist/api/controllers/view/viewBuilder.js +15 -1
  176. package/dist/api/routes/application.js +1 -0
  177. package/dist/api/routes/dev.js +8 -4
  178. package/dist/api/routes/query.js +3 -1
  179. package/dist/api/routes/tests/utilities/TestFunctions.js +7 -9
  180. package/dist/automations/actions.js +9 -3
  181. package/dist/automations/automationUtils.js +8 -29
  182. package/dist/automations/steps/bash.js +2 -1
  183. package/dist/automations/steps/createRow.js +1 -1
  184. package/dist/automations/steps/deleteRow.js +2 -1
  185. package/dist/automations/steps/executeQuery.js +2 -1
  186. package/dist/automations/steps/executeScript.js +2 -1
  187. package/dist/automations/steps/outgoingWebhook.js +2 -1
  188. package/dist/automations/steps/queryRows.js +2 -1
  189. package/dist/automations/steps/sendSmtpEmail.js +2 -1
  190. package/dist/automations/steps/updateRow.js +6 -2
  191. package/dist/automations/thread.js +14 -3
  192. package/dist/constants/index.js +1 -0
  193. package/dist/db/linkedRows/index.js +9 -6
  194. package/dist/integrations/base/sql.js +4 -4
  195. package/dist/integrations/mysql.js +1 -1
  196. package/dist/integrations/utils.js +17 -1
  197. package/dist/middleware/currentapp.js +8 -2
  198. package/dist/package.json +7 -6
  199. package/dist/tests/utilities/TestConfiguration.js +34 -19
  200. package/dist/tests/utilities/controllers.js +1 -0
  201. package/dist/tsconfig.tsbuildinfo +1 -1
  202. package/dist/utilities/fileSystem/index.js +49 -33
  203. package/dist/utilities/global.js +1 -1
  204. package/dist/utilities/queue/inMemoryQueue.js +6 -0
  205. package/dist/utilities/rowProcessor/index.js +8 -0
  206. package/dist/utilities/scriptRunner.js +19 -0
  207. package/package.json +8 -7
  208. package/scripts/jestSetup.js +2 -0
  209. package/src/api/controllers/analytics.js +4 -0
  210. package/src/api/controllers/application.js +43 -9
  211. package/src/api/controllers/auth.js +12 -3
  212. package/src/api/controllers/backup.js +4 -5
  213. package/src/api/controllers/deploy/index.js +3 -18
  214. package/src/api/controllers/dev.js +16 -8
  215. package/src/api/controllers/permission.js +1 -0
  216. package/src/api/controllers/query.js +46 -11
  217. package/src/api/controllers/row/ExternalRequest.ts +53 -4
  218. package/src/api/controllers/script.js +4 -19
  219. package/src/api/controllers/table/index.js +5 -12
  220. package/src/api/controllers/table/utils.js +94 -12
  221. package/src/api/controllers/templates.js +17 -5
  222. package/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +1 -1
  223. package/src/api/controllers/view/viewBuilder.js +14 -1
  224. package/src/api/routes/application.js +1 -0
  225. package/src/api/routes/dev.js +9 -4
  226. package/src/api/routes/query.js +3 -1
  227. package/src/api/routes/tests/auth.spec.js +1 -2
  228. package/src/api/routes/tests/permissions.spec.js +3 -0
  229. package/src/api/routes/tests/query.spec.js +2 -2
  230. package/src/api/routes/tests/routing.spec.js +24 -8
  231. package/src/api/routes/tests/user.spec.js +1 -1
  232. package/src/api/routes/tests/utilities/TestFunctions.js +7 -9
  233. package/src/api/routes/tests/webhook.spec.js +4 -1
  234. package/src/automations/actions.js +10 -3
  235. package/src/automations/automationUtils.js +7 -31
  236. package/src/automations/steps/bash.js +2 -1
  237. package/src/automations/steps/createRow.js +1 -1
  238. package/src/automations/steps/deleteRow.js +2 -1
  239. package/src/automations/steps/executeQuery.js +2 -1
  240. package/src/automations/steps/executeScript.js +2 -1
  241. package/src/automations/steps/outgoingWebhook.js +2 -1
  242. package/src/automations/steps/queryRows.js +2 -1
  243. package/src/automations/steps/sendSmtpEmail.js +2 -1
  244. package/src/automations/steps/updateRow.js +6 -6
  245. package/src/automations/thread.js +17 -3
  246. package/src/constants/index.js +1 -0
  247. package/src/db/linkedRows/index.js +14 -7
  248. package/src/integrations/base/sql.ts +11 -3
  249. package/src/integrations/mysql.ts +1 -1
  250. package/src/integrations/tests/mongo.spec.js +10 -6
  251. package/src/integrations/tests/mysql.spec.js +1 -1
  252. package/src/integrations/utils.ts +18 -0
  253. package/src/middleware/currentapp.js +11 -2
  254. package/src/middleware/tests/currentapp.spec.js +3 -3
  255. package/src/tests/utilities/TestConfiguration.js +47 -19
  256. package/src/tests/utilities/controllers.js +1 -0
  257. package/src/utilities/fileSystem/index.js +65 -43
  258. package/src/utilities/global.js +1 -1
  259. package/src/utilities/queue/inMemoryQueue.js +7 -0
  260. package/src/utilities/rowProcessor/index.js +10 -0
  261. package/src/utilities/scriptRunner.js +21 -0
@@ -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 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).
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 exports.exportDB(appId, {
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
- * exports a DB to either file or a variable (memory).
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 - the file name to export to, if not in memory.
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 Either the file stream or the variable (if no export name provided).
135
+ * @return {*} either a readable stream or a string
121
136
  */
122
- exports.exportDB = (dbName, { exportName, filter } = { exportName: undefined, filter: undefined }) => __awaiter(void 0, void 0, void 0, function* () {
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
- yield instanceDb.dump(stream, {
137
- filter,
138
- });
139
- // just in memory, return the final string
140
- if (!exportName) {
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
- // write the file to the object store
144
- if (env.SELF_HOSTED) {
145
- yield streamUpload(ObjectStoreBuckets.BACKUPS, join(dbName, exportName), fs.createReadStream(path));
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
- return fs.createReadStream(path);
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.
@@ -33,7 +33,7 @@ exports.updateAppRole = (appId, user) => {
33
33
  user.roleId = BUILTIN_ROLE_IDS.ADMIN;
34
34
  }
35
35
  else if (!user.roleId) {
36
- user.roleId = BUILTIN_ROLE_IDS.BASIC;
36
+ user.roleId = BUILTIN_ROLE_IDS.PUBLIC;
37
37
  }
38
38
  delete user.roles;
39
39
  return user;
@@ -87,5 +87,11 @@ class InMemoryQueue {
87
87
  // TODO: implement for testing
88
88
  console.log(cronJobId);
89
89
  }
90
+ /**
91
+ * Implemented for tests
92
+ */
93
+ getRepeatableJobs() {
94
+ return [];
95
+ }
90
96
  }
91
97
  module.exports = InMemoryQueue;
@@ -149,6 +149,10 @@ 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
+ }
152
156
  // eslint-disable-next-line no-prototype-builtins
153
157
  if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
154
158
  return TYPE_TRANSFORM_MAP[type][row];
@@ -190,6 +194,10 @@ exports.inputProcessing = (user = {}, table, row, opts = { noAutoRelationships:
190
194
  clonedRow[key] = exports.coerce(value, field.type);
191
195
  }
192
196
  }
197
+ if (!clonedRow._id || !clonedRow._rev) {
198
+ clonedRow._id = row._id;
199
+ clonedRow._rev = row._rev;
200
+ }
193
201
  // handle auto columns - this returns an object like {table, row}
194
202
  return processAutoColumn(user, copiedTable, clonedRow, opts);
195
203
  };
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ const fetch = require("node-fetch");
3
+ const { VM, VMScript } = require("vm2");
4
+ class ScriptRunner {
5
+ constructor(script, context) {
6
+ const code = `let fn = () => {\n${script}\n}; results.out = fn();`;
7
+ this.vm = new VM();
8
+ this.results = { out: "" };
9
+ this.vm.setGlobals(context);
10
+ this.vm.setGlobal("fetch", fetch);
11
+ this.vm.setGlobal("results", this.results);
12
+ this.script = new VMScript(code);
13
+ }
14
+ execute() {
15
+ this.vm.run(this.script);
16
+ return this.results.out;
17
+ }
18
+ }
19
+ module.exports = ScriptRunner;
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.172",
4
+ "version": "0.9.173-alpha.3",
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.172",
72
- "@budibase/client": "^0.9.172",
73
- "@budibase/string-templates": "^0.9.172",
71
+ "@budibase/auth": "^0.9.173-alpha.3",
72
+ "@budibase/client": "^0.9.173-alpha.3",
73
+ "@budibase/string-templates": "^0.9.173-alpha.3",
74
74
  "@elastic/elasticsearch": "7.10.0",
75
75
  "@koa/router": "8.0.0",
76
76
  "@sendgrid/mail": "7.1.1",
77
- "@sentry/node": "5.19.2",
77
+ "@sentry/node": "^6.0.0",
78
78
  "airtable": "0.10.1",
79
79
  "arangojs": "7.2.0",
80
80
  "aws-sdk": "^2.767.0",
@@ -103,7 +103,7 @@
103
103
  "memorystream": "^0.3.1",
104
104
  "mongodb": "3.6.3",
105
105
  "mssql": "6.2.3",
106
- "mysql": "2.18.1",
106
+ "mysql2": "^2.3.1",
107
107
  "node-fetch": "2.6.0",
108
108
  "open": "7.3.0",
109
109
  "pg": "8.5.1",
@@ -119,6 +119,7 @@
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",
122
123
  "yargs": "13.2.4",
123
124
  "zlib": "1.0.5"
124
125
  },
@@ -148,5 +149,5 @@
148
149
  "typescript": "^4.3.5",
149
150
  "update-dotenv": "^1.1.1"
150
151
  },
151
- "gitHead": "f33cc5a9cd747ffb8491af36ac7baf0bf66de290"
152
+ "gitHead": "7bbd460f86bae19117a7a7ed48dc984aa6eb7826"
152
153
  }
@@ -8,3 +8,5 @@ env._set("CLIENT_ID", "test-client-id")
8
8
  env._set("BUDIBASE_DIR", tmpdir("budibase-unittests"))
9
9
  env._set("LOG_LEVEL", "silent")
10
10
  env._set("PORT", 0)
11
+
12
+ global.console.log = jest.fn() // console.log are ignored in tests
@@ -21,6 +21,10 @@ exports.endUserPing = async ctx => {
21
21
  return
22
22
  }
23
23
 
24
+ posthogClient.identify({
25
+ distinctId: ctx.user && ctx.user._id,
26
+ properties: {},
27
+ })
24
28
  posthogClient.capture({
25
29
  event: "budibase:end_user_ping",
26
30
  distinctId: ctx.user && ctx.user._id,
@@ -25,7 +25,12 @@ const { BASE_LAYOUTS } = require("../../constants/layouts")
25
25
  const { createHomeScreen } = require("../../constants/screens")
26
26
  const { cloneDeep } = require("lodash/fp")
27
27
  const { processObject } = require("@budibase/string-templates")
28
- const { getAllApps } = require("@budibase/auth/db")
28
+ const {
29
+ getAllApps,
30
+ isDevAppID,
31
+ getDeployedAppID,
32
+ Replication,
33
+ } = require("@budibase/auth/db")
29
34
  const { USERS_TABLE_SCHEMA } = require("../../constants")
30
35
  const {
31
36
  getDeployedApps,
@@ -134,7 +139,7 @@ async function createInstance(template) {
134
139
  return { _id: appId }
135
140
  }
136
141
 
137
- exports.fetch = async function (ctx) {
142
+ exports.fetch = async ctx => {
138
143
  const dev = ctx.query && ctx.query.status === AppStatus.DEV
139
144
  const all = ctx.query && ctx.query.status === AppStatus.ALL
140
145
  const apps = await getAllApps(CouchDB, { dev, all })
@@ -159,7 +164,7 @@ exports.fetch = async function (ctx) {
159
164
  ctx.body = apps
160
165
  }
161
166
 
162
- exports.fetchAppDefinition = async function (ctx) {
167
+ exports.fetchAppDefinition = async ctx => {
163
168
  const db = new CouchDB(ctx.params.appId)
164
169
  const layouts = await getLayouts(db)
165
170
  const userRoleId = getUserRoleId(ctx)
@@ -175,7 +180,7 @@ exports.fetchAppDefinition = async function (ctx) {
175
180
  }
176
181
  }
177
182
 
178
- exports.fetchAppPackage = async function (ctx) {
183
+ exports.fetchAppPackage = async ctx => {
179
184
  const db = new CouchDB(ctx.params.appId)
180
185
  const application = await db.get(DocumentTypes.APP_METADATA)
181
186
  const layouts = await getLayouts(db)
@@ -196,7 +201,7 @@ exports.fetchAppPackage = async function (ctx) {
196
201
  }
197
202
  }
198
203
 
199
- exports.create = async function (ctx) {
204
+ exports.create = async ctx => {
200
205
  const { useTemplate, templateKey, templateString } = ctx.request.body
201
206
  const instanceConfig = {
202
207
  useTemplate,
@@ -252,13 +257,13 @@ exports.create = async function (ctx) {
252
257
  ctx.body = newApplication
253
258
  }
254
259
 
255
- exports.update = async function (ctx) {
260
+ exports.update = async ctx => {
256
261
  const data = await updateAppPackage(ctx, ctx.request.body, ctx.params.appId)
257
262
  ctx.status = 200
258
263
  ctx.body = data
259
264
  }
260
265
 
261
- exports.updateClient = async function (ctx) {
266
+ exports.updateClient = async ctx => {
262
267
  // Get current app version
263
268
  const db = new CouchDB(ctx.params.appId)
264
269
  const application = await db.get(DocumentTypes.APP_METADATA)
@@ -280,7 +285,7 @@ exports.updateClient = async function (ctx) {
280
285
  ctx.body = data
281
286
  }
282
287
 
283
- exports.revertClient = async function (ctx) {
288
+ exports.revertClient = async ctx => {
284
289
  // Check app can be reverted
285
290
  const db = new CouchDB(ctx.params.appId)
286
291
  const application = await db.get(DocumentTypes.APP_METADATA)
@@ -303,7 +308,7 @@ exports.revertClient = async function (ctx) {
303
308
  ctx.body = data
304
309
  }
305
310
 
306
- exports.delete = async function (ctx) {
311
+ exports.delete = async ctx => {
307
312
  const db = new CouchDB(ctx.params.appId)
308
313
 
309
314
  const result = await db.destroy()
@@ -318,6 +323,35 @@ exports.delete = async function (ctx) {
318
323
  ctx.body = result
319
324
  }
320
325
 
326
+ exports.sync = async ctx => {
327
+ const appId = ctx.params.appId
328
+ if (!isDevAppID(appId)) {
329
+ ctx.throw(400, "This action cannot be performed for production apps")
330
+ }
331
+ const prodAppId = getDeployedAppID(appId)
332
+ const replication = new Replication({
333
+ source: prodAppId,
334
+ target: appId,
335
+ })
336
+ let error
337
+ try {
338
+ await replication.replicate({
339
+ filter: function (doc) {
340
+ return doc._id !== DocumentTypes.APP_METADATA
341
+ },
342
+ })
343
+ } catch (err) {
344
+ error = err
345
+ }
346
+ if (error) {
347
+ ctx.throw(400, error)
348
+ } else {
349
+ ctx.body = {
350
+ message: "App sync completed successfully.",
351
+ }
352
+ }
353
+ }
354
+
321
355
  const updateAppPackage = async (ctx, appPackage, appId) => {
322
356
  const url = await getAppUrlIfNotInUse(ctx)
323
357
  const db = new CouchDB(appId)
@@ -28,14 +28,23 @@ exports.fetchSelf = async ctx => {
28
28
  ...metadata,
29
29
  })
30
30
  } catch (err) {
31
+ let response
31
32
  // user didn't exist in app, don't pretend they do
32
33
  if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
33
- ctx.body = {}
34
+ response = {}
34
35
  }
35
36
  // user has a role of some sort, return them
36
- else {
37
- ctx.body = user
37
+ else if (err.status === 404) {
38
+ const metadata = {
39
+ _id: userId,
40
+ }
41
+ const dbResp = await db.put(metadata)
42
+ user._rev = dbResp.rev
43
+ response = user
44
+ } else {
45
+ response = user
38
46
  }
47
+ ctx.body = response
39
48
  }
40
49
  } else {
41
50
  ctx.body = user
@@ -1,10 +1,9 @@
1
- const { performBackup } = require("../../utilities/fileSystem")
1
+ const { streamBackup } = require("../../utilities/fileSystem")
2
2
 
3
3
  exports.exportAppDump = async function (ctx) {
4
4
  const { appId } = ctx.query
5
- const appname = decodeURI(ctx.query.appname)
6
- const backupIdentifier = `${appname}Backup${new Date().getTime()}.txt`
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 performBackup(appId, backupIdentifier)
8
+ ctx.body = await streamBackup(appId)
10
9
  }
@@ -1,6 +1,6 @@
1
1
  const CouchDB = require("../../../db")
2
2
  const Deployment = require("./Deployment")
3
- const { Replication } = require("@budibase/auth/db")
3
+ const { Replication, getDeployedAppID } = require("@budibase/auth/db")
4
4
  const { DocumentTypes, getAutomationParams } = require("../../../db/utils")
5
5
  const {
6
6
  disableAllCrons,
@@ -87,7 +87,7 @@ async function initDeployedApp(prodAppId) {
87
87
 
88
88
  async function deployApp(deployment) {
89
89
  try {
90
- const productionAppId = deployment.appId.replace("_dev", "")
90
+ const productionAppId = getDeployedAppID(deployment.appId)
91
91
 
92
92
  const replication = new Replication({
93
93
  source: deployment.appId,
@@ -104,23 +104,8 @@ async function deployApp(deployment) {
104
104
  appDoc.instance._id = productionAppId
105
105
  await db.put(appDoc)
106
106
  console.log("New app doc written successfully.")
107
-
108
- console.log("Setting up live repl between dev and prod")
109
- // Set up live sync between the live and dev instances
110
- const liveReplication = new Replication({
111
- source: productionAppId,
112
- target: deployment.appId,
113
- })
114
- liveReplication.subscribe({
115
- filter: function (doc) {
116
- return doc._id !== DocumentTypes.APP_METADATA
117
- },
118
- })
119
- console.log("Set up live repl between dev and prod")
120
-
121
- console.log("Initialising deployed app")
122
107
  await initDeployedApp(productionAppId)
123
- console.log("Init complete, setting deployment to successful")
108
+ console.log("Deployed app initialised, setting deployment to successful")
124
109
  deployment.setStatus(DeploymentStatus.SUCCESS)
125
110
  await storeDeploymentHistory(deployment)
126
111
  } catch (err) {
@@ -7,11 +7,13 @@ 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, path = "global") {
11
11
  const { devPath } = ctx.params
12
12
  const queryString = ctx.originalUrl.split("?")[1] || ""
13
13
  const response = await fetch(
14
- checkSlashesInUrl(`${env.WORKER_URL}/api/global/${devPath}?${queryString}`),
14
+ checkSlashesInUrl(
15
+ `${env.WORKER_URL}/api/${path}/${devPath}?${queryString}`
16
+ ),
15
17
  request(
16
18
  ctx,
17
19
  {
@@ -41,16 +43,22 @@ async function redirect(ctx, method) {
41
43
  ctx.cookies
42
44
  }
43
45
 
44
- exports.redirectGet = async ctx => {
45
- await redirect(ctx, "GET")
46
+ exports.buildRedirectGet = path => {
47
+ return async ctx => {
48
+ await redirect(ctx, "GET", path)
49
+ }
46
50
  }
47
51
 
48
- exports.redirectPost = async ctx => {
49
- await redirect(ctx, "POST")
52
+ exports.buildRedirectPost = path => {
53
+ return async ctx => {
54
+ await redirect(ctx, "POST", path)
55
+ }
50
56
  }
51
57
 
52
- exports.redirectDelete = async ctx => {
53
- await redirect(ctx, "DELETE")
58
+ exports.buildRedirectDelete = path => {
59
+ return async ctx => {
60
+ await redirect(ctx, "DELETE", path)
61
+ }
54
62
  }
55
63
 
56
64
  exports.clearLock = async ctx => {
@@ -147,6 +147,7 @@ exports.getResourcePerms = async function (ctx) {
147
147
  const rolePerms = role.permissions
148
148
  if (
149
149
  rolePerms &&
150
+ rolePerms[resourceId] &&
150
151
  (rolePerms[resourceId] === level ||
151
152
  rolePerms[resourceId].indexOf(level) !== -1)
152
153
  ) {
@@ -4,6 +4,7 @@ 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")
7
8
 
8
9
  // simple function to append "readable" to all read queries
9
10
  function enrichQueries(input) {
@@ -28,12 +29,39 @@ function formatResponse(resp) {
28
29
  resp = { response: resp }
29
30
  }
30
31
  }
31
- if (!Array.isArray(resp)) {
32
- resp = [resp]
33
- }
34
32
  return resp
35
33
  }
36
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]
52
+ }
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 }
63
+ }
64
+
37
65
  exports.fetch = async function (ctx) {
38
66
  const db = new CouchDB(ctx.appId)
39
67
 
@@ -122,15 +150,16 @@ exports.preview = async function (ctx) {
122
150
  ctx.throw(400, "Integration type does not exist.")
123
151
  }
124
152
 
125
- const { fields, parameters, queryVerb } = ctx.request.body
126
-
153
+ const { fields, parameters, queryVerb, transformer } = ctx.request.body
127
154
  const enrichedQuery = await enrichQueryFields(fields, parameters)
128
-
129
155
  const integration = new Integration(datasource.config)
130
- const rows = formatResponse(await integration[queryVerb](enrichedQuery))
131
156
 
132
- // get all the potential fields in the schema
133
- const keys = rows.flatMap(Object.keys)
157
+ const { rows, keys } = await runAndTransform(
158
+ integration,
159
+ queryVerb,
160
+ enrichedQuery,
161
+ transformer
162
+ )
134
163
 
135
164
  ctx.body = {
136
165
  rows,
@@ -158,10 +187,16 @@ exports.execute = async function (ctx) {
158
187
  query.fields,
159
188
  ctx.request.body.parameters
160
189
  )
161
-
162
190
  const integration = new Integration(datasource.config)
191
+
163
192
  // call the relevant CRUD method on the integration class
164
- ctx.body = formatResponse(await integration[query.queryVerb](enrichedQuery))
193
+ const { rows } = await runAndTransform(
194
+ integration,
195
+ query.queryVerb,
196
+ enrichedQuery,
197
+ query.transformer
198
+ )
199
+ ctx.body = rows
165
200
  // cleanup
166
201
  if (integration.end) {
167
202
  integration.end()