@budibase/server 0.9.185-alpha.1 → 0.9.185-alpha.13

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 (234) hide show
  1. package/Dockerfile +1 -1
  2. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  3. package/builder/assets/index.4a7c5636.css +1 -0
  4. package/builder/assets/index.dc495306.js +1317 -0
  5. package/builder/assets/vendor.470042a4.js +152 -0
  6. package/builder/assets/vendor.5e1fe644.css +1 -0
  7. package/builder/index.html +4 -4
  8. package/coverage/clover.xml +689 -598
  9. package/coverage/coverage-final.json +89 -87
  10. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  11. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  12. package/coverage/lcov-report/api/controllers/application.js.html +43 -13
  13. package/coverage/lcov-report/api/controllers/auth.js.html +7 -7
  14. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  15. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  18. package/coverage/lcov-report/api/controllers/datasource.js.html +129 -21
  19. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  20. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  21. package/coverage/lcov-report/api/controllers/index.html +83 -83
  22. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
  24. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  25. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  26. package/coverage/lcov-report/api/controllers/query.js.html +48 -210
  27. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  28. package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/row/external.js.html +2 -2
  30. package/coverage/lcov-report/api/controllers/row/index.html +10 -10
  31. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  32. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  33. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  34. package/coverage/lcov-report/api/controllers/row/utils.js.html +2 -2
  35. package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  37. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/index.html +11 -11
  39. package/coverage/lcov-report/api/controllers/table/index.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/table/internal.js.html +2 -2
  41. package/coverage/lcov-report/api/controllers/table/utils.js.html +8 -8
  42. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  43. package/coverage/lcov-report/api/controllers/user.js.html +33 -18
  44. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/index.html +9 -9
  46. package/coverage/lcov-report/api/controllers/view/index.js.html +5 -5
  47. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  48. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  49. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  50. package/coverage/lcov-report/api/index.html +1 -1
  51. package/coverage/lcov-report/api/index.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  63. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.html +4 -4
  65. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  71. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  77. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  82. package/coverage/lcov-report/automations/actions.js.html +2 -2
  83. package/coverage/lcov-report/automations/automationUtils.js.html +3 -3
  84. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  85. package/coverage/lcov-report/automations/index.html +33 -48
  86. package/coverage/lcov-report/automations/index.js.html +2 -2
  87. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  92. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  94. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/index.html +14 -14
  96. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  99. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  104. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  106. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  107. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  109. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  114. package/coverage/lcov-report/automations/utils.js.html +42 -12
  115. package/coverage/lcov-report/constants/index.html +5 -5
  116. package/coverage/lcov-report/constants/index.js.html +25 -4
  117. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  118. package/coverage/lcov-report/constants/screens.js.html +1 -1
  119. package/coverage/lcov-report/db/client.js.html +2 -2
  120. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  121. package/coverage/lcov-report/db/index.html +2 -2
  122. package/coverage/lcov-report/db/index.js.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  124. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  125. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  126. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  127. package/coverage/lcov-report/db/newid.js.html +1 -1
  128. package/coverage/lcov-report/db/utils.js.html +1 -1
  129. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  130. package/coverage/lcov-report/events/index.html +6 -6
  131. package/coverage/lcov-report/events/index.js.html +1 -1
  132. package/coverage/lcov-report/events/utils.js.html +5 -5
  133. package/coverage/lcov-report/index.html +83 -68
  134. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  135. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  136. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  137. package/coverage/lcov-report/middleware/authorized.js.html +4 -4
  138. package/coverage/lcov-report/middleware/builder.js.html +16 -7
  139. package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
  140. package/coverage/lcov-report/middleware/index.html +21 -21
  141. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  142. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  143. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  144. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  145. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  146. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +83 -32
  147. package/coverage/lcov-report/threads/index.html +141 -0
  148. package/coverage/lcov-report/threads/index.js.html +260 -0
  149. package/coverage/lcov-report/threads/query.js.html +269 -0
  150. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  151. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  152. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  153. package/coverage/lcov-report/utilities/csvParser.js.html +2 -2
  154. package/coverage/lcov-report/utilities/global.js.html +2 -2
  155. package/coverage/lcov-report/utilities/index.html +21 -21
  156. package/coverage/lcov-report/utilities/index.js.html +12 -9
  157. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
  158. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  159. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  160. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  161. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  162. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  163. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  164. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  165. package/coverage/lcov-report/utilities/security.js.html +3 -3
  166. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  167. package/coverage/lcov-report/utilities/users.js.html +1 -1
  168. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  169. package/coverage/lcov.info +1136 -972
  170. package/dist/api/controllers/application.js +11 -1
  171. package/dist/api/controllers/datasource.js +31 -7
  172. package/dist/api/controllers/deploy/index.js +2 -0
  173. package/dist/api/controllers/dev.js +4 -1
  174. package/dist/api/controllers/query.js +27 -57
  175. package/dist/api/controllers/user.js +10 -6
  176. package/dist/api/routes/query.js +1 -1
  177. package/dist/app.js +2 -11
  178. package/dist/automations/bullboard.js +37 -10
  179. package/dist/automations/triggers.js +7 -3
  180. package/dist/automations/utils.js +11 -2
  181. package/dist/constants/index.js +6 -0
  182. package/dist/integrations/base/IntegrationBase.js +2 -0
  183. package/dist/integrations/base/sql.js +36 -8
  184. package/dist/integrations/mysql.js +3 -1
  185. package/dist/integrations/utils.js +7 -1
  186. package/dist/middleware/builder.js +4 -1
  187. package/dist/package.json +7 -6
  188. package/dist/{automations/thread.js → threads/automation.js} +23 -7
  189. package/dist/threads/index.js +58 -0
  190. package/dist/threads/query.js +61 -0
  191. package/dist/tsconfig.tsbuildinfo +1 -1
  192. package/dist/utilities/index.js +1 -0
  193. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  194. package/dist/utilities/scriptRunner.js +4 -1
  195. package/package.json +8 -7
  196. package/scripts/likeCypress.ts +33 -0
  197. package/src/api/controllers/application.js +11 -1
  198. package/src/api/controllers/datasource.js +45 -9
  199. package/src/api/controllers/deploy/index.js +2 -0
  200. package/src/api/controllers/dev.js +4 -1
  201. package/src/api/controllers/query.js +26 -80
  202. package/src/api/controllers/user.js +15 -10
  203. package/src/api/routes/query.js +1 -0
  204. package/src/app.ts +2 -10
  205. package/src/automations/bullboard.js +28 -11
  206. package/src/automations/tests/automation.spec.js +3 -4
  207. package/src/automations/triggers.js +7 -3
  208. package/src/automations/utils.js +12 -2
  209. package/src/constants/index.js +7 -0
  210. package/src/integrations/airtable.ts +2 -1
  211. package/src/integrations/arangodb.ts +2 -1
  212. package/src/integrations/base/IntegrationBase.ts +6 -0
  213. package/src/integrations/base/datasourcePlus.ts +2 -1
  214. package/src/integrations/base/sql.ts +38 -9
  215. package/src/integrations/couchdb.ts +2 -1
  216. package/src/integrations/dynamodb.ts +2 -1
  217. package/src/integrations/elasticsearch.ts +2 -1
  218. package/src/integrations/mongodb.ts +2 -1
  219. package/src/integrations/mysql.ts +1 -1
  220. package/src/integrations/rest.ts +2 -1
  221. package/src/integrations/s3.ts +2 -1
  222. package/src/integrations/utils.ts +6 -1
  223. package/src/middleware/builder.js +4 -1
  224. package/src/{automations/thread.js → threads/automation.js} +24 -7
  225. package/src/threads/index.js +60 -0
  226. package/src/threads/query.js +63 -0
  227. package/src/utilities/index.js +1 -0
  228. package/src/utilities/queue/inMemoryQueue.js +7 -0
  229. package/src/utilities/scriptRunner.js +4 -1
  230. package/tsconfig.json +1 -1
  231. package/builder/assets/index.4feb6e69.js +0 -201
  232. package/builder/assets/index.a79ec0ef.css +0 -29
  233. package/builder/assets/vendor.278a305c.js +0 -9
  234. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -27,6 +27,8 @@ const { getAllLocks } = require("../../utilities/redis");
27
27
  const { updateClientLibrary, backupClientLibrary, revertClientLibrary, } = require("../../utilities/fileSystem/clientLibrary");
28
28
  const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy");
29
29
  const { syncGlobalUsers } = require("./user");
30
+ const { app: appCache } = require("@budibase/auth/cache");
31
+ const { cleanupAutomations } = require("../../automations/utils");
30
32
  const URL_REGEX_SLASH = /\/|\\/g;
31
33
  // utility function, need to do away with this
32
34
  function getLayouts(db) {
@@ -214,6 +216,7 @@ exports.create = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
214
216
  if (!env.isTest()) {
215
217
  yield createApp(appId);
216
218
  }
219
+ yield appCache.invalidateAppMetadata(appId, newApplication);
217
220
  ctx.status = 200;
218
221
  ctx.body = newApplication;
219
222
  });
@@ -268,8 +271,12 @@ exports.delete = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
268
271
  if (!env.isTest() && !ctx.query.unpublish) {
269
272
  yield deleteApp(ctx.params.appId);
270
273
  }
274
+ if (ctx.query && ctx.query.unpublish) {
275
+ yield cleanupAutomations(ctx.params.appId);
276
+ }
271
277
  // make sure the app/role doesn't stick around after the app has been deleted
272
278
  yield removeAppFromUserRoles(ctx, ctx.params.appId);
279
+ yield appCache.invalidateAppMetadata(ctx.params.appId);
273
280
  ctx.status = 200;
274
281
  ctx.body = result;
275
282
  });
@@ -331,7 +338,10 @@ const updateAppPackage = (ctx, appPackage, appId) => __awaiter(void 0, void 0, v
331
338
  // the locked by property is attached by server but generated from
332
339
  // Redis, shouldn't ever store it
333
340
  delete newAppPackage.lockedBy;
334
- return yield db.put(newAppPackage);
341
+ const response = yield db.put(newAppPackage);
342
+ // remove any cached metadata, so that it will be updated
343
+ yield appCache.invalidateAppMetadata(appId);
344
+ return response;
335
345
  });
336
346
  const createEmptyAppPackage = (ctx, app) => __awaiter(void 0, void 0, void 0, function* () {
337
347
  const db = new CouchDB(app.appId);
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  const CouchDB = require("../../db");
12
12
  const { generateDatasourceID, getDatasourceParams, getQueryParams, DocumentTypes, BudibaseInternalDB, getTableParams, } = require("../../db/utils");
13
- const { BuildSchemaErrors } = require("../../constants");
13
+ const { BuildSchemaErrors, InvalidColumns } = require("../../constants");
14
14
  const { integrations } = require("../../integrations");
15
15
  const { getDatasourceAndQuery } = require("./row/utils");
16
16
  exports.fetch = function (ctx) {
@@ -97,8 +97,12 @@ exports.destroy = function (ctx) {
97
97
  return __awaiter(this, void 0, void 0, function* () {
98
98
  const db = new CouchDB(ctx.appId);
99
99
  // Delete all queries for the datasource
100
- const rows = yield db.allDocs(getQueryParams(ctx.params.datasourceId, null));
101
- yield db.bulkDocs(rows.rows.map(row => (Object.assign(Object.assign({}, row.doc), { _deleted: true }))));
100
+ const queries = yield db.allDocs(getQueryParams(ctx.params.datasourceId, null));
101
+ yield db.bulkDocs(queries.rows.map(row => ({
102
+ _id: row.id,
103
+ _rev: row.value.rev,
104
+ _deleted: true,
105
+ })));
102
106
  // delete the datasource
103
107
  yield db.remove(ctx.params.datasourceId, ctx.params.revId);
104
108
  ctx.message = `Datasource deleted.`;
@@ -123,6 +127,21 @@ exports.query = function (ctx) {
123
127
  }
124
128
  });
125
129
  };
130
+ function getErrorTables(errors, errorType) {
131
+ return Object.entries(errors)
132
+ .filter(entry => entry[1] === errorType)
133
+ .map(([name]) => name);
134
+ }
135
+ function updateError(error, newError, tables) {
136
+ if (!error) {
137
+ error = "";
138
+ }
139
+ if (error.length > 0) {
140
+ error += "\n";
141
+ }
142
+ error += `${newError} ${tables.join(", ")}`;
143
+ return error;
144
+ }
126
145
  const buildSchemaHelper = (datasource) => __awaiter(void 0, void 0, void 0, function* () {
127
146
  const Connector = integrations[datasource.source];
128
147
  // Connect to the DB and build the schema
@@ -142,10 +161,15 @@ const buildSchemaHelper = (datasource) => __awaiter(void 0, void 0, void 0, func
142
161
  const errors = connector.schemaErrors;
143
162
  let error = null;
144
163
  if (errors && Object.keys(errors).length > 0) {
145
- const noKeyTables = Object.entries(errors)
146
- .filter(entry => entry[1] === BuildSchemaErrors.NO_KEY)
147
- .map(([name]) => name);
148
- error = `No primary key constraint found for the following: ${noKeyTables.join(", ")}`;
164
+ const noKey = getErrorTables(errors, BuildSchemaErrors.NO_KEY);
165
+ const invalidCol = getErrorTables(errors, BuildSchemaErrors.INVALID_COLUMN);
166
+ if (noKey.length) {
167
+ error = updateError(error, "No primary key constraint found for the following:", noKey);
168
+ }
169
+ if (invalidCol.length) {
170
+ const invalidCols = Object.values(InvalidColumns).join(", ");
171
+ error = updateError(error, `Cannot use columns ${invalidCols} found in following:`, invalidCol);
172
+ }
149
173
  }
150
174
  return { tables: connector.tables, error };
151
175
  });
@@ -13,6 +13,7 @@ const Deployment = require("./Deployment");
13
13
  const { Replication, getDeployedAppID } = require("@budibase/auth/db");
14
14
  const { DocumentTypes, getAutomationParams } = require("../../../db/utils");
15
15
  const { disableAllCrons, enableCronTrigger, } = require("../../../automations/utils");
16
+ const { app: appCache } = require("@budibase/auth/cache");
16
17
  // the max time we can wait for an invalidation to complete before considering it failed
17
18
  const MAX_PENDING_TIME_MS = 30 * 60000;
18
19
  const DeploymentStatus = {
@@ -95,6 +96,7 @@ function deployApp(deployment) {
95
96
  appDoc.appId = productionAppId;
96
97
  appDoc.instance._id = productionAppId;
97
98
  yield db.put(appDoc);
99
+ yield appCache.invalidateAppMetadata(productionAppId);
98
100
  console.log("New app doc written successfully.");
99
101
  yield initDeployedApp(productionAppId);
100
102
  console.log("Deployed app initialised, setting deployment to successful");
@@ -16,6 +16,7 @@ const { request } = require("../../utilities/workerRequests");
16
16
  const { clearLock } = require("../../utilities/redis");
17
17
  const { Replication } = require("@budibase/auth").db;
18
18
  const { DocumentTypes } = require("../../db/utils");
19
+ const { app: appCache } = require("@budibase/auth/cache");
19
20
  function redirect(ctx, method, path = "global") {
20
21
  return __awaiter(this, void 0, void 0, function* () {
21
22
  const { devPath } = ctx.params;
@@ -25,7 +26,8 @@ function redirect(ctx, method, path = "global") {
25
26
  body: ctx.request.body,
26
27
  }, true));
27
28
  if (response.status !== 200) {
28
- ctx.throw(response.status, response.statusText);
29
+ const err = yield response.text();
30
+ ctx.throw(400, err);
29
31
  }
30
32
  const cookie = response.headers.get("set-cookie");
31
33
  if (cookie) {
@@ -102,6 +104,7 @@ exports.revert = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
102
104
  appDoc.appId = appId;
103
105
  appDoc.instance._id = appId;
104
106
  yield db.put(appDoc);
107
+ yield appCache.invalidateAppMetadata(appId);
105
108
  ctx.body = {
106
109
  message: "Reverted changes successfully.",
107
110
  };
@@ -11,10 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  const { processString } = require("@budibase/string-templates");
12
12
  const CouchDB = require("../../db");
13
13
  const { generateQueryID, getQueryParams } = require("../../db/utils");
14
- const { integrations } = require("../../integrations");
15
14
  const { BaseQueryVerbs } = require("../../constants");
16
15
  const env = require("../../environment");
17
- const ScriptRunner = require("../../utilities/scriptRunner");
16
+ const { Thread, ThreadType } = require("../../threads");
17
+ const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 });
18
18
  // simple function to append "readable" to all read queries
19
19
  function enrichQueries(input) {
20
20
  const wasArray = Array.isArray(input);
@@ -26,38 +26,6 @@ function enrichQueries(input) {
26
26
  }
27
27
  return wasArray ? queries : queries[0];
28
28
  }
29
- function formatResponse(resp) {
30
- if (typeof resp === "string") {
31
- try {
32
- resp = JSON.parse(resp);
33
- }
34
- catch (err) {
35
- resp = { response: resp };
36
- }
37
- }
38
- return resp;
39
- }
40
- function runAndTransform(integration, queryVerb, enrichedQuery, transformer) {
41
- return __awaiter(this, void 0, void 0, function* () {
42
- let rows = formatResponse(yield integration[queryVerb](enrichedQuery));
43
- // transform as required
44
- if (transformer) {
45
- const runner = new ScriptRunner(transformer, { data: rows });
46
- rows = runner.execute();
47
- }
48
- // needs to an array for next step
49
- if (!Array.isArray(rows)) {
50
- rows = [rows];
51
- }
52
- // map into JSON if just raw primitive here
53
- if (rows.find(row => typeof row !== "object")) {
54
- rows = rows.map(value => ({ value }));
55
- }
56
- // get all the potential fields in the schema
57
- let keys = rows.flatMap(Object.keys);
58
- return { rows, keys };
59
- });
60
- }
61
29
  exports.fetch = function (ctx) {
62
30
  return __awaiter(this, void 0, void 0, function* () {
63
31
  const db = new CouchDB(ctx.appId);
@@ -135,21 +103,22 @@ exports.preview = function (ctx) {
135
103
  return __awaiter(this, void 0, void 0, function* () {
136
104
  const db = new CouchDB(ctx.appId);
137
105
  const datasource = yield db.get(ctx.request.body.datasourceId);
138
- const Integration = integrations[datasource.source];
139
- if (!Integration) {
140
- ctx.throw(400, "Integration type does not exist.");
141
- }
142
106
  const { fields, parameters, queryVerb, transformer } = ctx.request.body;
143
107
  const enrichedQuery = yield enrichQueryFields(fields, parameters);
144
- const integration = new Integration(datasource.config);
145
- const { rows, keys } = yield runAndTransform(integration, queryVerb, enrichedQuery, transformer);
146
- ctx.body = {
147
- rows,
148
- schemaFields: [...new Set(keys)],
149
- };
150
- // cleanup
151
- if (integration.end) {
152
- integration.end();
108
+ try {
109
+ const { rows, keys } = yield Runner.run({
110
+ datasource,
111
+ queryVerb,
112
+ query: enrichedQuery,
113
+ transformer,
114
+ });
115
+ ctx.body = {
116
+ rows,
117
+ schemaFields: [...new Set(keys)],
118
+ };
119
+ }
120
+ catch (err) {
121
+ ctx.throw(400, err);
153
122
  }
154
123
  });
155
124
  };
@@ -158,18 +127,19 @@ exports.execute = function (ctx) {
158
127
  const db = new CouchDB(ctx.appId);
159
128
  const query = yield db.get(ctx.params.queryId);
160
129
  const datasource = yield db.get(query.datasourceId);
161
- const Integration = integrations[datasource.source];
162
- if (!Integration) {
163
- ctx.throw(400, "Integration type does not exist.");
164
- }
165
130
  const enrichedQuery = yield enrichQueryFields(query.fields, ctx.request.body.parameters);
166
- const integration = new Integration(datasource.config);
167
131
  // call the relevant CRUD method on the integration class
168
- const { rows } = yield runAndTransform(integration, query.queryVerb, enrichedQuery, query.transformer);
169
- ctx.body = rows;
170
- // cleanup
171
- if (integration.end) {
172
- integration.end();
132
+ try {
133
+ const { rows } = yield Runner.run({
134
+ datasource,
135
+ queryVerb: query.queryVerb,
136
+ query: enrichedQuery,
137
+ transformer: query.transformer,
138
+ });
139
+ ctx.body = rows;
140
+ }
141
+ catch (err) {
142
+ ctx.throw(400, err);
173
143
  }
174
144
  });
175
145
  };
@@ -94,6 +94,7 @@ exports.syncUser = function (ctx) {
94
94
  .map(([appId]) => appId);
95
95
  }
96
96
  for (let prodAppId of prodAppIds) {
97
+ const roleId = roles[prodAppId];
97
98
  const devAppId = getDevelopmentAppID(prodAppId);
98
99
  for (let appId of [prodAppId, devAppId]) {
99
100
  if (!(yield doesDatabaseExist(appId))) {
@@ -113,14 +114,17 @@ exports.syncUser = function (ctx) {
113
114
  tableId: InternalTables.USER_METADATA,
114
115
  };
115
116
  }
116
- let combined;
117
- if (deleting) {
118
- combined = Object.assign(Object.assign({}, metadata), { status: UserStatus.INACTIVE, metadata: BUILTIN_ROLE_IDS.PUBLIC });
117
+ // assign the roleId for the metadata doc
118
+ if (roleId) {
119
+ metadata.roleId = roleId;
119
120
  }
120
- else {
121
- combined = combineMetadataAndUser(user, metadata);
121
+ let combined = !deleting
122
+ ? combineMetadataAndUser(user, metadata)
123
+ : Object.assign(Object.assign({}, metadata), { status: UserStatus.INACTIVE, metadata: BUILTIN_ROLE_IDS.PUBLIC });
124
+ // if its null then there was no updates required
125
+ if (combined) {
126
+ yield db.put(combined);
122
127
  }
123
- yield db.put(combined);
124
128
  }
125
129
  }
126
130
  ctx.body = {
@@ -41,7 +41,7 @@ router
41
41
  .get("/api/queries", authorized(BUILDER), queryController.fetch)
42
42
  .post("/api/queries", bodySubResource("datasourceId", "_id"), authorized(BUILDER), generateQueryValidation(), queryController.save)
43
43
  .post("/api/queries/preview", bodyResource("datasourceId"), authorized(BUILDER), generateQueryPreviewValidation(), queryController.preview)
44
- .get("/api/queries/:queryId", authorized(PermissionTypes.QUERY, PermissionLevels.READ), queryController.find)
44
+ .get("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.READ), queryController.find)
45
45
  .post("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.execute)
46
46
  .delete("/api/queries/:queryId/:revId", paramResource("queryId"), authorized(BUILDER), queryController.destroy);
47
47
  module.exports = router;
package/dist/app.js CHANGED
@@ -41,17 +41,8 @@ app.use(pino({
41
41
  level: env.LOG_LEVEL || "error",
42
42
  }));
43
43
  if (!env.isTest()) {
44
- const bullApp = bullboard.init();
45
- app.use((ctx, next) => __awaiter(void 0, void 0, void 0, function* () {
46
- if (ctx.path.startsWith(bullboard.pathPrefix)) {
47
- ctx.status = 200;
48
- ctx.respond = false;
49
- bullApp(ctx.req, ctx.res);
50
- }
51
- else {
52
- yield next();
53
- }
54
- }));
44
+ const plugin = bullboard.init();
45
+ app.use(plugin);
55
46
  }
56
47
  app.context.eventEmitter = eventEmitter;
57
48
  app.context.auth = {};
@@ -1,7 +1,16 @@
1
1
  "use strict";
2
- const { createBullBoard } = require("bull-board");
3
- const { BullAdapter } = require("bull-board/bullAdapter");
4
- const express = require("express");
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ const { createBullBoard } = require("@bull-board/api");
12
+ const { BullAdapter } = require("@bull-board/api/bullAdapter");
13
+ const { KoaAdapter } = require("@bull-board/koa");
5
14
  const env = require("../environment");
6
15
  const Queue = env.isTest()
7
16
  ? require("../utilities/queue/inMemoryQueue")
@@ -9,19 +18,37 @@ const Queue = env.isTest()
9
18
  const { JobQueues } = require("../constants");
10
19
  const { utils } = require("@budibase/auth/redis");
11
20
  const { opts, redisProtocolUrl } = utils.getRedisOptions();
12
- const redisConfig = redisProtocolUrl || { redis: opts };
13
- let automationQueue = new Queue(JobQueues.AUTOMATIONS, redisConfig);
14
- exports.pathPrefix = "/bulladmin";
21
+ const CLEANUP_PERIOD_MS = 60 * 1000;
22
+ const queueConfig = redisProtocolUrl || { redis: opts };
23
+ let cleanupInternal = null;
24
+ let automationQueue = new Queue(JobQueues.AUTOMATIONS, queueConfig);
25
+ function cleanup() {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ yield automationQueue.clean(CLEANUP_PERIOD_MS, "completed");
28
+ });
29
+ }
30
+ const PATH_PREFIX = "/bulladmin";
15
31
  exports.init = () => {
16
- const expressApp = express();
32
+ // cleanup the events every 5 minutes
33
+ if (!cleanupInternal) {
34
+ cleanupInternal = setInterval(cleanup, CLEANUP_PERIOD_MS);
35
+ // fire off an initial cleanup
36
+ cleanup().catch(err => {
37
+ console.error(`Unable to cleanup automation queue initially - ${err}`);
38
+ });
39
+ }
17
40
  // Set up queues for bull board admin
18
41
  const queues = [automationQueue];
19
42
  const adapters = [];
43
+ const serverAdapter = new KoaAdapter();
20
44
  for (let queue of queues) {
21
45
  adapters.push(new BullAdapter(queue));
22
46
  }
23
- const { router } = createBullBoard(adapters);
24
- expressApp.use(exports.pathPrefix, router);
25
- return expressApp;
47
+ createBullBoard({
48
+ queues: adapters,
49
+ serverAdapter,
50
+ });
51
+ serverAdapter.setBasePath(PATH_PREFIX);
52
+ return serverAdapter.registerPlugin();
26
53
  };
27
54
  exports.queue = automationQueue;
@@ -20,6 +20,10 @@ const { checkTestFlag } = require("../utilities/redis");
20
20
  const utils = require("./utils");
21
21
  const env = require("../environment");
22
22
  const TRIGGER_DEFINITIONS = definitions;
23
+ const JOB_OPTS = {
24
+ removeOnComplete: true,
25
+ removeOnFail: true,
26
+ };
23
27
  function queueRelevantRowAutomations(event, eventType) {
24
28
  return __awaiter(this, void 0, void 0, function* () {
25
29
  if (event.appId == null) {
@@ -47,7 +51,7 @@ function queueRelevantRowAutomations(event, eventType) {
47
51
  }
48
52
  if (automationTrigger.inputs &&
49
53
  automationTrigger.inputs.tableId === event.row.tableId) {
50
- yield queue.add({ automation, event });
54
+ yield queue.add({ automation, event }, JOB_OPTS);
51
55
  }
52
56
  }
53
57
  });
@@ -85,7 +89,7 @@ exports.externalTrigger = function (automation, params, { getResponses } = {}) {
85
89
  automation.definition.trigger != null &&
86
90
  automation.definition.trigger.stepId === definitions.APP.stepId &&
87
91
  automation.definition.trigger.stepId === "APP" &&
88
- !checkTestFlag(automation._id)) {
92
+ !(yield checkTestFlag(automation._id))) {
89
93
  // values are likely to be submitted as strings, so we shall convert to correct type
90
94
  const coercedFields = {};
91
95
  const fields = automation.definition.trigger.inputs.fields;
@@ -99,7 +103,7 @@ exports.externalTrigger = function (automation, params, { getResponses } = {}) {
99
103
  return utils.processEvent({ data });
100
104
  }
101
105
  else {
102
- return queue.add(data);
106
+ return queue.add(data, JOB_OPTS);
103
107
  }
104
108
  });
105
109
  };
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- const runner = require("./thread");
11
+ const { Thread, ThreadType } = require("../threads");
12
12
  const { definitions } = require("./triggerInfo");
13
13
  const webhooks = require("../api/controllers/webhook");
14
14
  const CouchDB = require("../db");
@@ -19,10 +19,11 @@ const { MetadataTypes } = require("../constants");
19
19
  const { getDeployedAppID } = require("@budibase/auth/db");
20
20
  const WH_STEP_ID = definitions.WEBHOOK.stepId;
21
21
  const CRON_STEP_ID = definitions.CRON.stepId;
22
+ const Runner = new Thread(ThreadType.AUTOMATION);
22
23
  exports.processEvent = (job) => __awaiter(void 0, void 0, void 0, function* () {
23
24
  try {
24
25
  // need to actually await these so that an error can be captured properly
25
- return yield runner(job);
26
+ return yield Runner.run(job);
26
27
  }
27
28
  catch (err) {
28
29
  console.error(`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`);
@@ -146,3 +147,11 @@ exports.checkForWebhooks = ({ appId, oldAuto, newAuto }) => __awaiter(void 0, vo
146
147
  }
147
148
  return newAuto;
148
149
  });
150
+ /**
151
+ * When removing an app/unpublishing it need to make sure automations are cleaned up (cron).
152
+ * @param appId {string} the app that is being removed.
153
+ * @return {Promise<void>} clean is complete if this succeeds.
154
+ */
155
+ exports.cleanupAutomations = (appId) => __awaiter(void 0, void 0, void 0, function* () {
156
+ yield exports.disableAllCrons(appId);
157
+ });
@@ -150,8 +150,14 @@ exports.MetadataTypes = {
150
150
  AUTOMATION_TEST_INPUT: "automationTestInput",
151
151
  AUTOMATION_TEST_HISTORY: "automationTestHistory",
152
152
  };
153
+ exports.InvalidColumns = {
154
+ ID: "_id",
155
+ REV: "_rev",
156
+ TABLE_ID: "tableId",
157
+ };
153
158
  exports.BuildSchemaErrors = {
154
159
  NO_KEY: "no_key",
160
+ INVALID_COLUMN: "invalid_column",
155
161
  };
156
162
  // pass through the list from the auth/core lib
157
163
  exports.ObjectStoreBuckets = ObjectStoreBuckets;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -17,19 +17,46 @@ const datasource_1 = require("../../definitions/datasource");
17
17
  const utils_1 = require("../utils");
18
18
  const sqlTable_1 = __importDefault(require("./sqlTable"));
19
19
  const BASE_LIMIT = 5000;
20
+ // these are invalid dates sent by the client, need to convert them to a real max date
21
+ const MIN_ISO_DATE = "0000-00-00T00:00:00.000Z";
22
+ const MAX_ISO_DATE = "9999-00-00T00:00:00.000Z";
23
+ function parse(input) {
24
+ if (Array.isArray(input)) {
25
+ return JSON.stringify(input);
26
+ }
27
+ if (typeof input !== "string") {
28
+ return input;
29
+ }
30
+ if (input === MAX_ISO_DATE) {
31
+ return new Date(8640000000000000);
32
+ }
33
+ if (input === MIN_ISO_DATE) {
34
+ return new Date(-8640000000000000);
35
+ }
36
+ if (utils_1.isIsoDateString(input)) {
37
+ return new Date(input);
38
+ }
39
+ return input;
40
+ }
20
41
  function parseBody(body) {
21
42
  for (let [key, value] of Object.entries(body)) {
22
- if (Array.isArray(value)) {
23
- body[key] = JSON.stringify(value);
24
- }
25
- if (typeof value !== "string") {
26
- continue;
43
+ body[key] = parse(value);
44
+ }
45
+ return body;
46
+ }
47
+ function parseFilters(filters) {
48
+ for (let [key, value] of Object.entries(filters)) {
49
+ let parsed;
50
+ if (typeof value === "object") {
51
+ parsed = parseFilters(value);
27
52
  }
28
- if (utils_1.isIsoDateString(value)) {
29
- body[key] = new Date(value);
53
+ else {
54
+ parsed = parse(value);
30
55
  }
56
+ // @ts-ignore
57
+ filters[key] = parsed;
31
58
  }
32
- return body;
59
+ return filters;
33
60
  }
34
61
  class InternalBuilder {
35
62
  constructor(client) {
@@ -45,6 +72,7 @@ class InternalBuilder {
45
72
  if (!filters) {
46
73
  return query;
47
74
  }
75
+ filters = parseFilters(filters);
48
76
  // if all or specified in filters, then everything is an or
49
77
  const allOr = filters.allOr;
50
78
  if (filters.oneOf) {
@@ -147,7 +147,9 @@ var MySQLModule;
147
147
  });
148
148
  }
149
149
  read(query) {
150
- return internalQuery(this.client, utils_1.getSqlQuery(query));
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ return internalQuery(this.client, utils_1.getSqlQuery(query));
152
+ });
151
153
  }
152
154
  update(query) {
153
155
  return __awaiter(this, void 0, void 0, function* () {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.finaliseExternalTables = exports.isIsoDateString = exports.isSQL = exports.getSqlQuery = exports.convertSqlType = exports.breakRowIdField = exports.convertRowId = exports.isRowId = exports.generateRowIdField = exports.breakExternalTableId = exports.buildExternalTableId = exports.isExternalTable = exports.SqlClients = void 0;
4
4
  const datasource_1 = require("../definitions/datasource");
5
5
  const { DocumentTypes, SEPARATOR } = require("../db/utils");
6
- const { FieldTypes, BuildSchemaErrors } = require("../constants");
6
+ const { FieldTypes, BuildSchemaErrors, InvalidColumns } = require("../constants");
7
7
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`;
8
8
  const ROW_ID_REGEX = /^\[.*]$/g;
9
9
  const SQL_TYPE_MAP = {
@@ -151,14 +151,20 @@ function copyExistingPropsOver(tableName, table, entities) {
151
151
  return table;
152
152
  }
153
153
  function finaliseExternalTables(tables, entities) {
154
+ const invalidColumns = Object.values(InvalidColumns);
154
155
  const finalTables = {};
155
156
  const errors = {};
156
157
  for (let [name, table] of Object.entries(tables)) {
158
+ const schemaFields = Object.keys(table.schema);
157
159
  // make sure every table has a key
158
160
  if (table.primary == null || table.primary.length === 0) {
159
161
  errors[name] = BuildSchemaErrors.NO_KEY;
160
162
  continue;
161
163
  }
164
+ else if (schemaFields.find(field => invalidColumns.includes(field))) {
165
+ errors[name] = BuildSchemaErrors.INVALID_COLUMN;
166
+ continue;
167
+ }
162
168
  // make sure all previous props have been added back
163
169
  finalTables[name] = copyExistingPropsOver(name, table, entities);
164
170
  }
@@ -13,6 +13,7 @@ const { doesUserHaveLock, updateLock, checkDebounce, setDebounce, } = require(".
13
13
  const CouchDB = require("../db");
14
14
  const { DocumentTypes } = require("../db/utils");
15
15
  const { PermissionTypes } = require("@budibase/auth/permissions");
16
+ const { app: appCache } = require("@budibase/auth/cache");
16
17
  const DEBOUNCE_TIME_SEC = 30;
17
18
  /************************************************** *
18
19
  * This middleware has been broken out of the *
@@ -51,7 +52,9 @@ function updateAppUpdatedAt(ctx) {
51
52
  const db = new CouchDB(appId);
52
53
  const metadata = yield db.get(DocumentTypes.APP_METADATA);
53
54
  metadata.updatedAt = new Date().toISOString();
54
- yield db.put(metadata);
55
+ const response = yield db.put(metadata);
56
+ metadata._rev = response.rev;
57
+ yield appCache.invalidateAppMetadata(appId, metadata);
55
58
  // set a new debounce record with a short TTL
56
59
  yield setDebounce(appId, DEBOUNCE_TIME_SEC);
57
60
  });