@budibase/server 1.3.13 → 1.3.14

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 (187) hide show
  1. package/builder/assets/{index.e50bda9f.css → index.a70e216a.css} +1 -1
  2. package/builder/assets/{index.fb22f106.js → index.d8428ac8.js} +166 -165
  3. package/builder/index.html +2 -2
  4. package/coverage/clover.xml +90 -68
  5. package/coverage/coverage-final.json +7 -7
  6. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  7. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/backup.js.html +8 -5
  10. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/index.html +7 -7
  15. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  44. package/coverage/lcov-report/api/index.html +1 -1
  45. package/coverage/lcov-report/api/index.js.html +1 -1
  46. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/index.html +1 -1
  57. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  71. package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  74. package/coverage/lcov-report/automations/actions.js.html +1 -1
  75. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  76. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  77. package/coverage/lcov-report/automations/index.html +24 -24
  78. package/coverage/lcov-report/automations/index.js.html +18 -9
  79. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/index.html +1 -1
  86. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  96. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggers.js.html +126 -24
  105. package/coverage/lcov-report/constants/index.html +1 -1
  106. package/coverage/lcov-report/constants/index.js.html +1 -1
  107. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  108. package/coverage/lcov-report/constants/screens.js.html +1 -1
  109. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  110. package/coverage/lcov-report/db/index.html +1 -1
  111. package/coverage/lcov-report/db/index.js.html +1 -1
  112. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  113. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  114. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  116. package/coverage/lcov-report/db/newid.js.html +1 -1
  117. package/coverage/lcov-report/db/utils.js.html +5 -5
  118. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  119. package/coverage/lcov-report/events/index.html +1 -1
  120. package/coverage/lcov-report/events/index.js.html +1 -1
  121. package/coverage/lcov-report/events/utils.js.html +1 -1
  122. package/coverage/lcov-report/index.html +34 -34
  123. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  124. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  125. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  126. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  127. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  128. package/coverage/lcov-report/middleware/index.html +1 -1
  129. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  130. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  131. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  132. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  133. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  134. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  135. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  136. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  137. package/coverage/lcov-report/utilities/global.js.html +1 -1
  138. package/coverage/lcov-report/utilities/index.html +19 -19
  139. package/coverage/lcov-report/utilities/index.js.html +40 -16
  140. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  141. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  142. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  143. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  144. package/coverage/lcov-report/utilities/rowProcessor/index.html +5 -5
  145. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  146. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +4 -4
  147. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  148. package/coverage/lcov-report/utilities/security.js.html +1 -1
  149. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  150. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  151. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +6 -3
  152. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  153. package/coverage/lcov-report/utilities/users.js.html +1 -1
  154. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  155. package/coverage/lcov.info +167 -127
  156. package/dist/api/controllers/application.js +1 -5
  157. package/dist/api/controllers/backup.js +2 -1
  158. package/dist/api/controllers/deploy/index.js +17 -2
  159. package/dist/api/controllers/row/ExternalRequest.js +1 -1
  160. package/dist/automations/index.js +10 -5
  161. package/dist/automations/triggers.js +39 -5
  162. package/dist/automations/utils.js +16 -7
  163. package/dist/integrations/rest.js +16 -1
  164. package/dist/package.json +6 -6
  165. package/dist/threads/automation.js +6 -0
  166. package/dist/tsconfig.build.tsbuildinfo +1 -1
  167. package/dist/utilities/fileSystem/index.js +6 -6
  168. package/dist/utilities/index.js +8 -0
  169. package/dist/utilities/rowProcessor/utils.js +1 -1
  170. package/dist/utilities/usageQuota/rows.js +2 -1
  171. package/package.json +7 -7
  172. package/src/api/controllers/application.ts +1 -5
  173. package/src/api/controllers/backup.js +2 -1
  174. package/src/api/controllers/deploy/index.ts +17 -4
  175. package/src/api/controllers/row/ExternalRequest.ts +1 -1
  176. package/src/api/routes/tests/query.spec.js +3 -3
  177. package/src/automations/index.js +6 -3
  178. package/src/automations/triggers.js +44 -10
  179. package/src/automations/utils.ts +21 -9
  180. package/src/definitions/datasource.ts +1 -0
  181. package/src/integrations/rest.ts +14 -1
  182. package/src/integrations/tests/rest.spec.js +38 -3
  183. package/src/threads/automation.ts +6 -0
  184. package/src/utilities/fileSystem/index.js +6 -6
  185. package/src/utilities/index.js +8 -0
  186. package/src/utilities/rowProcessor/utils.js +1 -1
  187. package/src/utilities/usageQuota/rows.js +2 -1
@@ -110,11 +110,11 @@ exports.defineFilter = excludeRows => {
110
110
  * data or user relationships.
111
111
  * @param {string} appId The app to backup
112
112
  * @param {object} config Config to send to export DB
113
- * @param {boolean} includeRows Flag to state whether the export should include data.
113
+ * @param {boolean} excludeRows Flag to state whether the export should include data.
114
114
  * @returns {*} either a string or a stream of the backup
115
115
  */
116
- const backupAppData = (appId, config, includeRows) => __awaiter(void 0, void 0, void 0, function* () {
117
- return yield exports.exportDB(appId, Object.assign(Object.assign({}, config), { filter: exports.defineFilter(includeRows) }));
116
+ const backupAppData = (appId, config, excludeRows) => __awaiter(void 0, void 0, void 0, function* () {
117
+ return yield exports.exportDB(appId, Object.assign(Object.assign({}, config), { filter: exports.defineFilter(excludeRows) }));
118
118
  });
119
119
  /**
120
120
  * Takes a copy of the database state for an app to the object store.
@@ -128,11 +128,11 @@ exports.performBackup = (appId, backupName) => __awaiter(void 0, void 0, void 0,
128
128
  /**
129
129
  * Streams a backup of the database state for an app
130
130
  * @param {string} appId The ID of the app which is to be backed up.
131
- * @param {boolean} includeRows Flag to state whether the export should include data.
131
+ * @param {boolean} excludeRows Flag to state whether the export should include data.
132
132
  * @returns {*} a readable stream of the backup which is written in real time
133
133
  */
134
- exports.streamBackup = (appId, includeRows) => __awaiter(void 0, void 0, void 0, function* () {
135
- return yield backupAppData(appId, { stream: true }, includeRows);
134
+ exports.streamBackup = (appId, excludeRows) => __awaiter(void 0, void 0, void 0, function* () {
135
+ return yield backupAppData(appId, { stream: true }, excludeRows);
136
136
  });
137
137
  /**
138
138
  * Exports a DB to either file or a variable (memory).
@@ -150,3 +150,11 @@ exports.convertBookmark = bookmark => {
150
150
  }
151
151
  return bookmark;
152
152
  };
153
+ exports.isQsTrue = param => {
154
+ if (typeof param === "string") {
155
+ return param.toLowerCase() === "true";
156
+ }
157
+ else {
158
+ return param === true;
159
+ }
160
+ };
@@ -64,7 +64,7 @@ exports.processDates = (table, rows) => {
64
64
  if (schema.type !== FieldTypes.DATETIME) {
65
65
  continue;
66
66
  }
67
- if (!schema.ignoreTimezones) {
67
+ if (!schema.timeOnly && !schema.ignoreTimezones) {
68
68
  datesWithTZ.push(column);
69
69
  }
70
70
  }
@@ -72,7 +72,8 @@ exports.getUniqueRows = (appIds) => __awaiter(void 0, void 0, void 0, function*
72
72
  // ensure uniqueness on a per app pair basis
73
73
  // this can't be done on all rows because app import results in
74
74
  // duplicate row ids across apps
75
- uniqueRows = uniqueRows.concat(...new Set(appRows));
75
+ // the array pre-concat is important to avoid stack overflow
76
+ uniqueRows = uniqueRows.concat([...new Set(appRows)]);
76
77
  }
77
78
  return uniqueRows;
78
79
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.3.13",
4
+ "version": "1.3.14",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -77,11 +77,11 @@
77
77
  "license": "GPL-3.0",
78
78
  "dependencies": {
79
79
  "@apidevtools/swagger-parser": "10.0.3",
80
- "@budibase/backend-core": "^1.3.13",
81
- "@budibase/client": "^1.3.13",
82
- "@budibase/pro": "1.3.12",
83
- "@budibase/string-templates": "^1.3.13",
84
- "@budibase/types": "^1.3.13",
80
+ "@budibase/backend-core": "^1.3.14",
81
+ "@budibase/client": "^1.3.14",
82
+ "@budibase/pro": "1.3.13",
83
+ "@budibase/string-templates": "^1.3.14",
84
+ "@budibase/types": "^1.3.14",
85
85
  "@bull-board/api": "3.7.0",
86
86
  "@bull-board/koa": "3.9.4",
87
87
  "@elastic/elasticsearch": "7.10.0",
@@ -196,5 +196,5 @@
196
196
  "optionalDependencies": {
197
197
  "oracledb": "5.3.0"
198
198
  },
199
- "gitHead": "272a2ec7c7f2fabf74153fc8573ce6745134cdde"
199
+ "gitHead": "a38626cf662d66aa8c04fb1d42dc7bc353ce7c31"
200
200
  }
@@ -553,11 +553,7 @@ export const sync = async (ctx: any, next: any) => {
553
553
  })
554
554
  let error
555
555
  try {
556
- await replication.replicate({
557
- filter: function (doc: any) {
558
- return doc._id !== DocumentType.APP_METADATA
559
- },
560
- })
556
+ await replication.replicate(replication.appReplicateOpts())
561
557
  } catch (err) {
562
558
  error = err
563
559
  } finally {
@@ -1,11 +1,12 @@
1
1
  const { streamBackup } = require("../../utilities/fileSystem")
2
2
  const { events, context } = require("@budibase/backend-core")
3
3
  const { DocumentType } = require("../../db/utils")
4
+ const { isQsTrue } = require("../../utilities")
4
5
 
5
6
  exports.exportAppDump = async function (ctx) {
6
7
  let { appId, excludeRows } = ctx.query
7
8
  const appName = decodeURI(ctx.query.appname)
8
- excludeRows = excludeRows === "true"
9
+ excludeRows = isQsTrue(excludeRows)
9
10
  const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt`
10
11
  ctx.attachment(backupIdentifier)
11
12
  ctx.body = await streamBackup(appId, excludeRows)
@@ -15,6 +15,7 @@ import {
15
15
  getAppId,
16
16
  getAppDB,
17
17
  getProdAppDB,
18
+ getDevAppDB,
18
19
  } from "@budibase/backend-core/context"
19
20
  import { quotas } from "@budibase/pro"
20
21
  import { events } from "@budibase/backend-core"
@@ -110,17 +111,29 @@ async function deployApp(deployment: any) {
110
111
  target: productionAppId,
111
112
  }
112
113
  replication = new Replication(config)
113
-
114
+ const devDb = getDevAppDB()
115
+ console.log("Compacting development DB")
116
+ await devDb.compact()
114
117
  console.log("Replication object created")
115
- await replication.replicate()
118
+ await replication.replicate(replication.appReplicateOpts())
116
119
  console.log("replication complete.. replacing app meta doc")
120
+ // app metadata is excluded as it is likely to be in conflict
121
+ // replicate the app metadata document manually
117
122
  const db = getProdAppDB()
118
- const appDoc = await db.get(DocumentType.APP_METADATA)
123
+ const appDoc = await devDb.get(DocumentType.APP_METADATA)
124
+ try {
125
+ const prodAppDoc = await db.get(DocumentType.APP_METADATA)
126
+ appDoc._rev = prodAppDoc._rev
127
+ } catch (err) {
128
+ delete appDoc._rev
129
+ }
119
130
 
131
+ // switch to production app ID
120
132
  deployment.appUrl = appDoc.url
121
-
122
133
  appDoc.appId = productionAppId
123
134
  appDoc.instance._id = productionAppId
135
+ // remove automation errors if they exist
136
+ delete appDoc.automationErrors
124
137
  await db.put(appDoc)
125
138
  await appCache.invalidateAppMetadata(productionAppId)
126
139
  console.log("New app doc written successfully.")
@@ -534,7 +534,7 @@ module External {
534
534
  })
535
535
  // this is the response from knex if no rows found
536
536
  const rows = !response[0].read ? response : []
537
- const storeTo = isMany ? field.throughFrom || linkPrimaryKey : manyKey
537
+ const storeTo = isMany ? field.throughFrom || linkPrimaryKey : fieldName
538
538
  related[storeTo] = { rows, isMany, tableId }
539
539
  }
540
540
  return related
@@ -311,7 +311,7 @@ describe("/queries", () => {
311
311
  "url": "string",
312
312
  "value": "string"
313
313
  })
314
- expect(res.body.rows[0].url).toContain("doctype html")
314
+ expect(res.body.rows[0].url).toContain("doctype%20html")
315
315
  })
316
316
 
317
317
  it("check that it automatically retries on fail with cached dynamics", async () => {
@@ -396,7 +396,7 @@ describe("/queries", () => {
396
396
  "queryHdr": userDetails.firstName,
397
397
  "secondHdr" : "1234"
398
398
  })
399
- expect(res.body.rows[0].url).toEqual("http://www.google.com?email=" + userDetails.email)
399
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?email=" + userDetails.email.replace("@", "%40"))
400
400
  })
401
401
 
402
402
  it("should bind the current user to query parameters", async () => {
@@ -413,7 +413,7 @@ describe("/queries", () => {
413
413
  "testParam" : "1234"
414
414
  })
415
415
 
416
- expect(res.body.rows[0].url).toEqual("http://www.google.com?test=" + userDetails.email +
416
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?test=" + userDetails.email.replace("@", "%40") +
417
417
  "&testName=" + userDetails.firstName + "&testParam=1234")
418
418
  })
419
419
 
@@ -1,16 +1,19 @@
1
1
  const { processEvent } = require("./utils")
2
2
  const { queue, shutdown } = require("./bullboard")
3
- const { TRIGGER_DEFINITIONS } = require("./triggers")
3
+ const { TRIGGER_DEFINITIONS, rebootTrigger } = require("./triggers")
4
4
  const { ACTION_DEFINITIONS } = require("./actions")
5
5
 
6
6
  /**
7
7
  * This module is built purely to kick off the worker farm and manage the inputs/outputs
8
8
  */
9
- exports.init = function () {
9
+ exports.init = async function () {
10
10
  // this promise will not complete
11
- return queue.process(async job => {
11
+ const promise = queue.process(async job => {
12
12
  await processEvent(job)
13
13
  })
14
+ // on init we need to trigger any reboot automations
15
+ await rebootTrigger()
16
+ return promise
14
17
  }
15
18
 
16
19
  exports.getQueues = () => {
@@ -9,6 +9,7 @@ const { checkTestFlag } = require("../utilities/redis")
9
9
  const utils = require("./utils")
10
10
  const env = require("../environment")
11
11
  const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
12
+ const { getAllApps } = require("@budibase/backend-core/db")
12
13
 
13
14
  const TRIGGER_DEFINITIONS = definitions
14
15
  const JOB_OPTS = {
@@ -16,24 +17,27 @@ const JOB_OPTS = {
16
17
  removeOnFail: true,
17
18
  }
18
19
 
20
+ async function getAllAutomations() {
21
+ const db = getAppDB()
22
+ let automations = await db.allDocs(
23
+ getAutomationParams(null, { include_docs: true })
24
+ )
25
+ return automations.rows.map(row => row.doc)
26
+ }
27
+
19
28
  async function queueRelevantRowAutomations(event, eventType) {
20
29
  if (event.appId == null) {
21
30
  throw `No appId specified for ${eventType} - check event emitters.`
22
31
  }
23
32
 
24
33
  doInAppContext(event.appId, async () => {
25
- const db = getAppDB()
26
- let automations = await db.allDocs(
27
- getAutomationParams(null, { include_docs: true })
28
- )
34
+ let automations = await getAllAutomations()
29
35
 
30
36
  // filter down to the correct event type
31
- automations = automations.rows
32
- .map(automation => automation.doc)
33
- .filter(automation => {
34
- const trigger = automation.definition.trigger
35
- return trigger && trigger.event === eventType
36
- })
37
+ automations = automations.filter(automation => {
38
+ const trigger = automation.definition.trigger
39
+ return trigger && trigger.event === eventType
40
+ })
37
41
 
38
42
  for (let automation of automations) {
39
43
  let automationDef = automation.definition
@@ -110,4 +114,34 @@ exports.externalTrigger = async function (
110
114
  }
111
115
  }
112
116
 
117
+ exports.rebootTrigger = async () => {
118
+ // reboot cron option is only available on the main thread at
119
+ // startup and only usable in self host
120
+ if (env.isInThread() || !env.SELF_HOSTED) {
121
+ return
122
+ }
123
+ // iterate through all production apps, find the reboot crons
124
+ // and trigger events for them
125
+ const appIds = await getAllApps({ dev: false, idsOnly: true })
126
+ for (let prodAppId of appIds) {
127
+ await doInAppContext(prodAppId, async () => {
128
+ let automations = await getAllAutomations()
129
+ let rebootEvents = []
130
+ for (let automation of automations) {
131
+ if (utils.isRebootTrigger(automation)) {
132
+ const job = {
133
+ automation,
134
+ event: {
135
+ appId: prodAppId,
136
+ timestamp: Date.now(),
137
+ },
138
+ }
139
+ rebootEvents.push(queue.add(job, JOB_OPTS))
140
+ }
141
+ }
142
+ await Promise.all(rebootEvents)
143
+ })
144
+ }
145
+ }
146
+
113
147
  exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS
@@ -17,6 +17,7 @@ import { tenancy } from "@budibase/backend-core"
17
17
  import { quotas } from "@budibase/pro"
18
18
  import { Automation } from "@budibase/types"
19
19
 
20
+ const REBOOT_CRON = "@reboot"
20
21
  const WH_STEP_ID = definitions.WEBHOOK.stepId
21
22
  const CRON_STEP_ID = definitions.CRON.stepId
22
23
  const Runner = new Thread(ThreadType.AUTOMATION)
@@ -109,22 +110,33 @@ export async function clearMetadata() {
109
110
  await db.bulkDocs(automationMetadata)
110
111
  }
111
112
 
113
+ export function isCronTrigger(auto: Automation) {
114
+ return (
115
+ auto &&
116
+ auto.definition.trigger &&
117
+ auto.definition.trigger.stepId === CRON_STEP_ID
118
+ )
119
+ }
120
+
121
+ export function isRebootTrigger(auto: Automation) {
122
+ const trigger = auto ? auto.definition.trigger : null
123
+ return isCronTrigger(auto) && trigger?.inputs.cron === REBOOT_CRON
124
+ }
125
+
112
126
  /**
113
127
  * This function handles checking of any cron jobs that need to be enabled/updated.
114
128
  * @param {string} appId The ID of the app in which we are checking for webhooks
115
129
  * @param {object|undefined} automation The automation object to be updated.
116
130
  */
117
- export async function enableCronTrigger(appId: any, automation: any) {
131
+ export async function enableCronTrigger(appId: any, automation: Automation) {
118
132
  const trigger = automation ? automation.definition.trigger : null
119
- function isCronTrigger(auto: any) {
120
- return (
121
- auto &&
122
- auto.definition.trigger &&
123
- auto.definition.trigger.stepId === CRON_STEP_ID
124
- )
125
- }
133
+
126
134
  // need to create cron job
127
- if (isCronTrigger(automation) && trigger?.inputs.cron) {
135
+ if (
136
+ isCronTrigger(automation) &&
137
+ !isRebootTrigger(automation) &&
138
+ trigger?.inputs.cron
139
+ ) {
128
140
  // make a job id rather than letting Bull decide, makes it easier to handle on way out
129
141
  const jobId = `${appId}_cron_${newid()}`
130
142
  const job: any = await queue.add(
@@ -34,6 +34,7 @@ export interface RestConfig {
34
34
  defaultHeaders: {
35
35
  [key: string]: any
36
36
  }
37
+ legacyHttpParser: boolean
37
38
  authConfigs: AuthConfig[]
38
39
  staticVariables: {
39
40
  [key: string]: string
@@ -14,6 +14,7 @@ import {
14
14
  BearerAuthConfig,
15
15
  } from "../definitions/datasource"
16
16
  import { get } from "lodash"
17
+ import qs from "querystring"
17
18
 
18
19
  const BodyTypes = {
19
20
  NONE: "none",
@@ -79,6 +80,12 @@ module RestModule {
79
80
  required: false,
80
81
  default: {},
81
82
  },
83
+ legacyHttpParser: {
84
+ display: "Legacy HTTP Support",
85
+ type: DatasourceFieldType.BOOLEAN,
86
+ required: false,
87
+ default: false,
88
+ },
82
89
  },
83
90
  query: {
84
91
  create: {
@@ -215,7 +222,8 @@ module RestModule {
215
222
  }
216
223
  }
217
224
 
218
- const main = `${path}?${queryString}`
225
+ // make sure the query string is fully encoded
226
+ const main = `${path}?${qs.encode(qs.decode(queryString))}`
219
227
  let complete = main
220
228
  if (this.config.url && !main.startsWith("http")) {
221
229
  complete = !this.config.url ? main : `${this.config.url}/${main}`
@@ -377,6 +385,11 @@ module RestModule {
377
385
  paginationValues
378
386
  )
379
387
 
388
+ if (this.config.legacyHttpParser) {
389
+ // https://github.com/nodejs/node/issues/43798
390
+ input.extraHttpOptions = { insecureHTTPParser: true }
391
+ }
392
+
380
393
  this.startTimeMs = performance.now()
381
394
  const url = this.getUrl(path, queryString, pagination, paginationValues)
382
395
  const response = await fetch(url, input)
@@ -50,7 +50,7 @@ describe("REST Integration", () => {
50
50
  name: "test",
51
51
  }),
52
52
  }
53
- const response = await config.integration.create(query)
53
+ await config.integration.create(query)
54
54
  expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
55
55
  method: "POST",
56
56
  body: '{"name":"test"}',
@@ -295,7 +295,7 @@ describe("REST Integration", () => {
295
295
  }
296
296
  await config.integration.read(query)
297
297
  expect(fetch).toHaveBeenCalledWith(
298
- `${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}&`,
298
+ `${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}`,
299
299
  {
300
300
  headers: {},
301
301
  method: "GET",
@@ -420,7 +420,7 @@ describe("REST Integration", () => {
420
420
  }
421
421
  const res = await config.integration.read(query)
422
422
  expect(fetch).toHaveBeenCalledWith(
423
- `${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}&`,
423
+ `${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}`,
424
424
  {
425
425
  headers: {},
426
426
  method: "GET",
@@ -528,5 +528,40 @@ describe("REST Integration", () => {
528
528
  expect(sentData.get(sizeParam)).toEqual(sizeValue.toString())
529
529
  expect(res.pagination.cursor).toEqual(123)
530
530
  })
531
+
532
+ it("should encode query string correctly", async () => {
533
+ const query = {
534
+ path: "api",
535
+ queryString: "test=1 2",
536
+ headers: HEADERS,
537
+ bodyType: "json",
538
+ requestBody: JSON.stringify({
539
+ name: "test",
540
+ }),
541
+ }
542
+ await config.integration.create(query)
543
+ expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1%202`, {
544
+ method: "POST",
545
+ body: '{"name":"test"}',
546
+ headers: HEADERS,
547
+ })
548
+ })
549
+ })
550
+
551
+ describe("Configuration options", () => {
552
+ it("Attaches insecureHttpParams when legacy HTTP Parser option is set", async () => {
553
+ config = new TestConfiguration({
554
+ url: BASE_URL,
555
+ legacyHttpParser: true
556
+ })
557
+ await config.integration.read({})
558
+ expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, {
559
+ method: "GET",
560
+ headers: {},
561
+ extraHttpOptions: {
562
+ insecureHTTPParser: true
563
+ }
564
+ })
565
+ })
531
566
  })
532
567
  })
@@ -458,6 +458,9 @@ class Orchestrator {
458
458
 
459
459
  export function execute(input: AutomationEvent, callback: WorkerCallback) {
460
460
  const appId = input.data.event.appId
461
+ if (!appId) {
462
+ throw new Error("Unable to execute, event doesn't contain app ID.")
463
+ }
461
464
  doInAppContext(appId, async () => {
462
465
  const automationOrchestrator = new Orchestrator(
463
466
  input.data.automation,
@@ -475,6 +478,9 @@ export function execute(input: AutomationEvent, callback: WorkerCallback) {
475
478
 
476
479
  export const removeStalled = async (input: AutomationEvent) => {
477
480
  const appId = input.data.event.appId
481
+ if (!appId) {
482
+ throw new Error("Unable to execute, event doesn't contain app ID.")
483
+ }
478
484
  await doInAppContext(appId, async () => {
479
485
  const automationOrchestrator = new Orchestrator(
480
486
  input.data.automation,
@@ -125,13 +125,13 @@ exports.defineFilter = excludeRows => {
125
125
  * data or user relationships.
126
126
  * @param {string} appId The app to backup
127
127
  * @param {object} config Config to send to export DB
128
- * @param {boolean} includeRows Flag to state whether the export should include data.
128
+ * @param {boolean} excludeRows Flag to state whether the export should include data.
129
129
  * @returns {*} either a string or a stream of the backup
130
130
  */
131
- const backupAppData = async (appId, config, includeRows) => {
131
+ const backupAppData = async (appId, config, excludeRows) => {
132
132
  return await exports.exportDB(appId, {
133
133
  ...config,
134
- filter: exports.defineFilter(includeRows),
134
+ filter: exports.defineFilter(excludeRows),
135
135
  })
136
136
  }
137
137
 
@@ -148,11 +148,11 @@ exports.performBackup = async (appId, backupName) => {
148
148
  /**
149
149
  * Streams a backup of the database state for an app
150
150
  * @param {string} appId The ID of the app which is to be backed up.
151
- * @param {boolean} includeRows Flag to state whether the export should include data.
151
+ * @param {boolean} excludeRows Flag to state whether the export should include data.
152
152
  * @returns {*} a readable stream of the backup which is written in real time
153
153
  */
154
- exports.streamBackup = async (appId, includeRows) => {
155
- return await backupAppData(appId, { stream: true }, includeRows)
154
+ exports.streamBackup = async (appId, excludeRows) => {
155
+ return await backupAppData(appId, { stream: true }, excludeRows)
156
156
  }
157
157
 
158
158
  /**
@@ -162,3 +162,11 @@ exports.convertBookmark = bookmark => {
162
162
  }
163
163
  return bookmark
164
164
  }
165
+
166
+ exports.isQsTrue = param => {
167
+ if (typeof param === "string") {
168
+ return param.toLowerCase() === "true"
169
+ } else {
170
+ return param === true
171
+ }
172
+ }
@@ -76,7 +76,7 @@ exports.processDates = (table, rows) => {
76
76
  if (schema.type !== FieldTypes.DATETIME) {
77
77
  continue
78
78
  }
79
- if (!schema.ignoreTimezones) {
79
+ if (!schema.timeOnly && !schema.ignoreTimezones) {
80
80
  datesWithTZ.push(column)
81
81
  }
82
82
  }
@@ -72,7 +72,8 @@ exports.getUniqueRows = async appIds => {
72
72
  // ensure uniqueness on a per app pair basis
73
73
  // this can't be done on all rows because app import results in
74
74
  // duplicate row ids across apps
75
- uniqueRows = uniqueRows.concat(...new Set(appRows))
75
+ // the array pre-concat is important to avoid stack overflow
76
+ uniqueRows = uniqueRows.concat([...new Set(appRows)])
76
77
  }
77
78
 
78
79
  return uniqueRows