@budibase/server 1.1.29 → 1.1.32-alpha.0

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 (208) hide show
  1. package/builder/assets/index.55714cd6.js +977 -0
  2. package/builder/assets/{index.2b959037.css → index.aa03d5f4.css} +2 -2
  3. package/builder/assets/{vendor.162098da.js → vendor.6bcafa60.js} +1 -1
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +407 -711
  6. package/coverage/coverage-final.json +15 -18
  7. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/index.html +1 -1
  16. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  22. package/coverage/lcov-report/api/controllers/query/validation.js.html +2 -2
  23. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/index.html +18 -18
  27. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +94 -49
  29. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  45. package/coverage/lcov-report/api/index.html +1 -1
  46. package/coverage/lcov-report/api/index.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/index.html +1 -1
  58. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  72. package/coverage/lcov-report/api/routes/utils/validators.js.html +2 -2
  73. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  75. package/coverage/lcov-report/automations/actions.js.html +1 -1
  76. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  77. package/coverage/lcov-report/automations/bullboard.js.html +17 -17
  78. package/coverage/lcov-report/automations/index.html +1 -1
  79. package/coverage/lcov-report/automations/index.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/executeQuery.js.html +5 -2
  84. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/index.html +25 -25
  87. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/queryRows.js.html +73 -10
  91. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/serverLog.js.html +27 -9
  93. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  106. package/coverage/lcov-report/constants/index.html +1 -1
  107. package/coverage/lcov-report/constants/index.js.html +5 -14
  108. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  109. package/coverage/lcov-report/constants/screens.js.html +1 -1
  110. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  111. package/coverage/lcov-report/db/index.html +19 -19
  112. package/coverage/lcov-report/db/index.js.html +1 -1
  113. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  114. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  116. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  117. package/coverage/lcov-report/db/newid.js.html +1 -1
  118. package/coverage/lcov-report/db/utils.js.html +61 -13
  119. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  120. package/coverage/lcov-report/events/index.html +1 -1
  121. package/coverage/lcov-report/events/index.js.html +1 -1
  122. package/coverage/lcov-report/events/utils.js.html +1 -1
  123. package/coverage/lcov-report/index.html +66 -81
  124. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  125. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  126. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  127. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  128. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  129. package/coverage/lcov-report/middleware/index.html +21 -21
  130. package/coverage/lcov-report/middleware/joi-validator.js.html +43 -43
  131. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  132. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  133. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  134. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  135. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  136. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  137. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  138. package/coverage/lcov-report/utilities/global.js.html +67 -13
  139. package/coverage/lcov-report/utilities/index.html +20 -20
  140. package/coverage/lcov-report/utilities/index.js.html +1 -1
  141. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +26 -14
  142. package/coverage/lcov-report/utilities/queue/index.html +17 -17
  143. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  144. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  145. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  146. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  147. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  148. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  149. package/coverage/lcov-report/utilities/security.js.html +2 -2
  150. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  151. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  152. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  153. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  154. package/coverage/lcov-report/utilities/users.js.html +1 -1
  155. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  156. package/coverage/lcov.info +747 -1245
  157. package/dist/api/controllers/deploy/index.js +1 -0
  158. package/dist/api/controllers/query/index.js +1 -1
  159. package/dist/api/controllers/query/validation.js +1 -1
  160. package/dist/api/controllers/row/internalSearch.js +30 -16
  161. package/dist/api/routes/utils/validators.js +1 -1
  162. package/dist/automations/steps/queryRows.js +21 -1
  163. package/dist/automations/steps/serverLog.js +8 -2
  164. package/dist/automations/utils.js +47 -1
  165. package/dist/constants/index.js +1 -4
  166. package/dist/db/utils.js +14 -1
  167. package/dist/definitions/automations.js +8 -0
  168. package/dist/integrations/base/sql.js +43 -14
  169. package/dist/package.json +8 -8
  170. package/dist/threads/automation.js +147 -54
  171. package/dist/threads/definitions.js +2 -0
  172. package/dist/threads/index.js +27 -20
  173. package/dist/threads/query.js +23 -15
  174. package/dist/threads/utils.js +47 -24
  175. package/dist/tsconfig.build.tsbuildinfo +1 -1
  176. package/dist/utilities/global.js +21 -4
  177. package/dist/utilities/queue/inMemoryQueue.js +5 -0
  178. package/package.json +9 -9
  179. package/src/api/controllers/deploy/index.ts +6 -1
  180. package/src/api/controllers/query/index.ts +1 -1
  181. package/src/api/controllers/query/validation.js +1 -1
  182. package/src/api/controllers/row/internalSearch.js +35 -20
  183. package/src/api/routes/tests/user.spec.js +2 -0
  184. package/src/api/routes/utils/validators.js +1 -1
  185. package/src/automations/steps/executeQuery.js +1 -0
  186. package/src/automations/steps/queryRows.js +22 -1
  187. package/src/automations/steps/serverLog.js +8 -2
  188. package/src/automations/tests/automation.spec.js +2 -2
  189. package/src/automations/utils.ts +54 -1
  190. package/src/constants/index.js +2 -5
  191. package/src/db/utils.js +16 -0
  192. package/src/definitions/automations.ts +49 -0
  193. package/src/definitions/datasource.ts +3 -0
  194. package/src/integrations/base/sql.ts +44 -13
  195. package/src/integrations/tests/sql.spec.js +50 -4
  196. package/src/middleware/tests/currentapp.spec.js +11 -0
  197. package/src/threads/{automation.js → automation.ts} +172 -48
  198. package/src/threads/definitions.ts +18 -0
  199. package/src/threads/index.ts +31 -23
  200. package/src/threads/{query.js → query.ts} +32 -12
  201. package/src/threads/{utils.js → utils.ts} +31 -12
  202. package/src/utilities/global.js +20 -2
  203. package/src/utilities/queue/inMemoryQueue.js +4 -0
  204. package/builder/assets/index.f116ac7f.js +0 -978
  205. package/coverage/lcov-report/threads/automation.js.html +0 -1114
  206. package/coverage/lcov-report/threads/index.html +0 -146
  207. package/coverage/lcov-report/threads/query.js.html +0 -916
  208. package/coverage/lcov-report/threads/utils.js.html +0 -319
@@ -1,5 +1,6 @@
1
1
  mockAuthWithNoCookie()
2
2
  mockWorker()
3
+ mockUserGroups()
3
4
 
4
5
  jest.mock("@budibase/backend-core/db", () => {
5
6
  const coreDb = jest.requireActual("@budibase/backend-core/db")
@@ -29,6 +30,16 @@ function mockReset() {
29
30
  mockWorker()
30
31
  }
31
32
 
33
+ function mockUserGroups() {
34
+ jest.mock("@budibase/pro", () => ({
35
+ groups: {
36
+ getGroupRoleId: () => {
37
+ return "BASIC"
38
+ },
39
+ },
40
+ }))
41
+ }
42
+
32
43
  function mockAuthWithNoCookie() {
33
44
  jest.resetModules()
34
45
  mockWorker()
@@ -1,36 +1,47 @@
1
- require("./utils").threadSetup()
2
- const actions = require("../automations/actions")
3
- const automationUtils = require("../automations/automationUtils")
4
- const AutomationEmitter = require("../events/AutomationEmitter")
5
- const { processObject } = require("@budibase/string-templates")
6
- const { DocumentTypes } = require("../db/utils")
7
- const { definitions: triggerDefs } = require("../automations/triggerInfo")
1
+ import { default as threadUtils } from "./utils"
2
+ threadUtils.threadSetup()
3
+ import { isRecurring, disableCron, isErrorInOutput } from "../automations/utils"
4
+ import { default as actions } from "../automations/actions"
5
+ import { default as automationUtils } from "../automations/automationUtils"
6
+ import { default as AutomationEmitter } from "../events/AutomationEmitter"
7
+ import { generateAutomationMetadataID, isProdAppID } from "../db/utils"
8
+ import { definitions as triggerDefs } from "../automations/triggerInfo"
9
+ import { AutomationErrors, MAX_AUTOMATION_RECURRING_ERRORS } from "../constants"
10
+ import { storeLog } from "../automations/logging"
11
+ import { Automation, AutomationStep, AutomationStatus } from "@budibase/types"
12
+ import {
13
+ LoopStep,
14
+ LoopStepTypes,
15
+ LoopInput,
16
+ AutomationEvent,
17
+ TriggerOutput,
18
+ AutomationContext,
19
+ AutomationMetadata,
20
+ } from "../definitions/automations"
21
+ import { WorkerCallback } from "./definitions"
8
22
  const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
9
- const { AutomationErrors, LoopStepTypes } = require("../constants")
10
- const { storeLog } = require("../automations/logging")
23
+ const { logAlertWithInfo, logWarn } = require("@budibase/backend-core/logging")
24
+ const { processObject } = require("@budibase/string-templates")
11
25
  const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
12
26
  const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId
13
-
14
27
  const CRON_STEP_ID = triggerDefs.CRON.stepId
15
- const STOPPED_STATUS = { success: true, status: "STOPPED" }
28
+ const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
16
29
  const { cloneDeep } = require("lodash/fp")
17
30
  const env = require("../environment")
18
31
 
19
- function typecastForLooping(loopStep, input) {
32
+ function typecastForLooping(loopStep: LoopStep, input: LoopInput) {
20
33
  if (!input || !input.binding) {
21
34
  return null
22
35
  }
23
- const isArray = Array.isArray(input.binding),
24
- isString = typeof input.binding === "string"
25
36
  try {
26
37
  switch (loopStep.inputs.option) {
27
38
  case LoopStepTypes.ARRAY:
28
- if (isString) {
39
+ if (typeof input.binding === "string") {
29
40
  return JSON.parse(input.binding)
30
41
  }
31
42
  break
32
43
  case LoopStepTypes.STRING:
33
- if (isArray) {
44
+ if (Array.isArray(input.binding)) {
34
45
  return input.binding.join(",")
35
46
  }
36
47
  break
@@ -41,7 +52,7 @@ function typecastForLooping(loopStep, input) {
41
52
  return input.binding
42
53
  }
43
54
 
44
- function getLoopIterations(loopStep, input) {
55
+ function getLoopIterations(loopStep: LoopStep, input: LoopInput) {
45
56
  const binding = typecastForLooping(loopStep, input)
46
57
  if (!loopStep || !binding) {
47
58
  return 1
@@ -57,11 +68,24 @@ function getLoopIterations(loopStep, input) {
57
68
  * inputs and handles any outputs.
58
69
  */
59
70
  class Orchestrator {
60
- constructor(automation, triggerOutput = {}) {
61
- this._metadata = triggerOutput.metadata
62
- this._chainCount = this._metadata ? this._metadata.automationChainCount : 0
63
- this._appId = triggerOutput.appId
64
- this._app = null
71
+ _chainCount: number
72
+ _appId: string
73
+ _automation: Automation
74
+ _emitter: any
75
+ _context: AutomationContext
76
+ _repeat?: { jobId: string; jobKey: string }
77
+ executionOutput: AutomationContext
78
+
79
+ constructor(automation: Automation, triggerOutput: TriggerOutput, opts: any) {
80
+ const metadata = triggerOutput.metadata
81
+ this._chainCount = metadata ? metadata.automationChainCount : 0
82
+ this._appId = triggerOutput.appId as string
83
+ if (opts?.repeat) {
84
+ this._repeat = {
85
+ jobId: opts.repeat.jobId,
86
+ jobKey: opts.repeat.key,
87
+ }
88
+ }
65
89
  const triggerStepId = automation.definition.trigger.stepId
66
90
  triggerOutput = this.cleanupTriggerOutputs(triggerStepId, triggerOutput)
67
91
  // remove from context
@@ -79,14 +103,14 @@ class Orchestrator {
79
103
  this.updateExecutionOutput(triggerId, triggerStepId, null, triggerOutput)
80
104
  }
81
105
 
82
- cleanupTriggerOutputs(stepId, triggerOutput) {
106
+ cleanupTriggerOutputs(stepId: string, triggerOutput: TriggerOutput) {
83
107
  if (stepId === CRON_STEP_ID) {
84
108
  triggerOutput.timestamp = Date.now()
85
109
  }
86
110
  return triggerOutput
87
111
  }
88
112
 
89
- async getStepFunctionality(stepId) {
113
+ async getStepFunctionality(stepId: string) {
90
114
  let step = await actions.getAction(stepId)
91
115
  if (step == null) {
92
116
  throw `Cannot find automation step by name ${stepId}`
@@ -94,25 +118,107 @@ class Orchestrator {
94
118
  return step
95
119
  }
96
120
 
97
- async getApp() {
98
- if (this._app) {
99
- return this._app
121
+ async getMetadata(): Promise<AutomationMetadata> {
122
+ const metadataId = generateAutomationMetadataID(this._automation._id)
123
+ const db = getAppDB()
124
+ let metadata: AutomationMetadata
125
+ try {
126
+ metadata = await db.get(metadataId)
127
+ } catch (err) {
128
+ metadata = {
129
+ _id: metadataId,
130
+ errorCount: 0,
131
+ }
132
+ }
133
+ return metadata
134
+ }
135
+
136
+ async checkIfShouldStop(metadata: AutomationMetadata): Promise<boolean> {
137
+ if (!metadata.errorCount || !this._repeat) {
138
+ return false
139
+ }
140
+ const automation = this._automation
141
+ const trigger = automation.definition.trigger
142
+ if (metadata.errorCount >= MAX_AUTOMATION_RECURRING_ERRORS) {
143
+ logWarn(
144
+ `CRON disabled due to errors - ${this._appId}/${this._automation._id}`
145
+ )
146
+ await disableCron(this._repeat?.jobId, this._repeat?.jobKey)
147
+ this.updateExecutionOutput(
148
+ trigger.id,
149
+ trigger.stepId,
150
+ {},
151
+ {
152
+ status: AutomationStatus.STOPPED_ERROR,
153
+ success: false,
154
+ }
155
+ )
156
+ await storeLog(automation, this.executionOutput)
157
+ return true
158
+ }
159
+ return false
160
+ }
161
+
162
+ async updateMetadata(metadata: AutomationMetadata) {
163
+ const output = this.executionOutput,
164
+ automation = this._automation
165
+ if (!output || !isRecurring(automation)) {
166
+ return
167
+ }
168
+ const count = metadata.errorCount
169
+ const isError = isErrorInOutput(output)
170
+ // nothing to do in this scenario, escape
171
+ if (!count && !isError) {
172
+ return
173
+ }
174
+ if (isError) {
175
+ metadata.errorCount = count ? count + 1 : 1
176
+ } else {
177
+ metadata.errorCount = 0
100
178
  }
101
179
  const db = getAppDB()
102
- this._app = await db.get(DocumentTypes.APP_METADATA)
103
- return this._app
180
+ try {
181
+ await db.put(metadata)
182
+ } catch (err) {
183
+ logAlertWithInfo(
184
+ "Failed to write automation metadata",
185
+ db.name,
186
+ automation._id,
187
+ err
188
+ )
189
+ }
104
190
  }
105
191
 
106
- updateExecutionOutput(id, stepId, inputs, outputs) {
192
+ updateExecutionOutput(id: string, stepId: string, inputs: any, outputs: any) {
107
193
  const stepObj = { id, stepId, inputs, outputs }
194
+ // replacing trigger when disabling CRON
195
+ if (
196
+ stepId === CRON_STEP_ID &&
197
+ outputs.status === AutomationStatus.STOPPED_ERROR
198
+ ) {
199
+ this.executionOutput.trigger = stepObj
200
+ this.executionOutput.steps = [stepObj]
201
+ return
202
+ }
108
203
  // first entry is always the trigger (constructor)
109
- if (this.executionOutput.steps.length === 0) {
204
+ if (
205
+ this.executionOutput.steps.length === 0 ||
206
+ this.executionOutput.trigger.id === id
207
+ ) {
110
208
  this.executionOutput.trigger = stepObj
111
209
  }
112
210
  this.executionOutput.steps.push(stepObj)
113
211
  }
114
212
 
115
- updateContextAndOutput(loopStepNumber, step, output, result) {
213
+ updateContextAndOutput(
214
+ loopStepNumber: number | undefined,
215
+ step: AutomationStep,
216
+ output: any,
217
+ result: { success: boolean; status: string }
218
+ ) {
219
+ if (!loopStepNumber) {
220
+ throw new Error("No loop step number provided.")
221
+ }
116
222
  this.executionOutput.steps.splice(loopStepNumber, 0, {
117
223
  id: step.id,
118
224
  stepId: step.stepId,
@@ -133,11 +239,22 @@ class Orchestrator {
133
239
  async execute() {
134
240
  let automation = this._automation
135
241
  let stopped = false
136
- let loopStep = null
242
+ let loopStep: AutomationStep | undefined = undefined
137
243
 
138
244
  let stepCount = 0
139
- let loopStepNumber = null
140
- let loopSteps = []
245
+ let loopStepNumber: any = undefined
246
+ let loopSteps: LoopStep[] | undefined = []
247
+ let metadata
248
+
249
+ // check if this is a recurring automation,
250
+ if (isProdAppID(this._appId) && isRecurring(automation)) {
251
+ metadata = await this.getMetadata()
252
+ const shouldStop = await this.checkIfShouldStop(metadata)
253
+ if (shouldStop) {
254
+ return
255
+ }
256
+ }
257
+
141
258
  for (let step of automation.definition.steps) {
142
259
  stepCount++
143
260
  let input,
@@ -151,7 +268,7 @@ class Orchestrator {
151
268
 
152
269
  if (loopStep) {
153
270
  input = await processObject(loopStep.inputs, this._context)
154
- iterations = getLoopIterations(loopStep, input)
271
+ iterations = getLoopIterations(loopStep as LoopStep, input)
155
272
  }
156
273
 
157
274
  for (let index = 0; index < iterations; index++) {
@@ -166,14 +283,17 @@ class Orchestrator {
166
283
 
167
284
  let tempOutput = { items: loopSteps, iterations: iterationCount }
168
285
  try {
169
- newInput.binding = typecastForLooping(loopStep, newInput)
286
+ newInput.binding = typecastForLooping(
287
+ loopStep as LoopStep,
288
+ newInput
289
+ )
170
290
  } catch (err) {
171
291
  this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
172
292
  status: AutomationErrors.INCORRECT_TYPE,
173
293
  success: false,
174
294
  })
175
- loopSteps = null
176
- loopStep = null
295
+ loopSteps = undefined
296
+ loopStep = undefined
177
297
  break
178
298
  }
179
299
 
@@ -223,8 +343,8 @@ class Orchestrator {
223
343
  status: AutomationErrors.MAX_ITERATIONS,
224
344
  success: true,
225
345
  })
226
- loopSteps = null
227
- loopStep = null
346
+ loopSteps = undefined
347
+ loopStep = undefined
228
348
  break
229
349
  }
230
350
 
@@ -232,7 +352,7 @@ class Orchestrator {
232
352
  const currentItem = this._context.steps[loopStepNumber]?.currentItem
233
353
  if (currentItem && typeof currentItem === "object") {
234
354
  isFailure = Object.keys(currentItem).some(value => {
235
- return currentItem[value] === loopStep.inputs.failure
355
+ return currentItem[value] === loopStep?.inputs.failure
236
356
  })
237
357
  } else {
238
358
  isFailure = currentItem && currentItem === loopStep.inputs.failure
@@ -243,8 +363,8 @@ class Orchestrator {
243
363
  status: AutomationErrors.FAILURE_CONDITION,
244
364
  success: false,
245
365
  })
246
- loopSteps = null
247
- loopStep = null
366
+ loopSteps = undefined
367
+ loopStep = undefined
248
368
  break
249
369
  }
250
370
  }
@@ -295,7 +415,7 @@ class Orchestrator {
295
415
  if (loopStep) {
296
416
  iterationCount++
297
417
  if (index === iterations - 1) {
298
- loopStep = null
418
+ loopStep = undefined
299
419
  this._context.steps.splice(loopStepNumber, 1)
300
420
  break
301
421
  }
@@ -316,22 +436,26 @@ class Orchestrator {
316
436
  })
317
437
 
318
438
  this._context.steps.splice(loopStepNumber, 0, tempOutput)
319
- loopSteps = null
439
+ loopSteps = undefined
320
440
  }
321
441
  }
322
442
 
323
443
  // store the logs for the automation run
324
444
  await storeLog(this._automation, this.executionOutput)
445
+ if (isProdAppID(this._appId) && isRecurring(automation) && metadata) {
446
+ await this.updateMetadata(metadata)
447
+ }
325
448
  return this.executionOutput
326
449
  }
327
450
  }
328
451
 
329
- module.exports = (input, callback) => {
452
+ export function execute(input: AutomationEvent, callback: WorkerCallback) {
330
453
  const appId = input.data.event.appId
331
454
  doInAppContext(appId, async () => {
332
455
  const automationOrchestrator = new Orchestrator(
333
456
  input.data.automation,
334
- input.data.event
457
+ input.data.event,
458
+ input.opts
335
459
  )
336
460
  try {
337
461
  const response = await automationOrchestrator.execute()
@@ -0,0 +1,18 @@
1
+ export type WorkerCallback = (error: any, response?: any) => void
2
+
3
+ export interface QueryEvent {
4
+ appId?: string
5
+ datasource: any
6
+ queryVerb: string
7
+ fields: { [key: string]: any }
8
+ parameters: { [key: string]: any }
9
+ pagination?: any
10
+ transformer: any
11
+ queryId: string
12
+ ctx?: any
13
+ }
14
+
15
+ export interface QueryVariable {
16
+ queryId: string
17
+ name: string
18
+ }
@@ -18,26 +18,23 @@ function typeToFile(type: any) {
18
18
  default:
19
19
  throw "Unknown thread type"
20
20
  }
21
+ // have to use require here, to make it work with worker-farm
21
22
  return require.resolve(filename)
22
23
  }
23
24
 
24
25
  export class Thread {
25
26
  type: any
26
27
  count: any
27
- disableThreading: any
28
28
  workers: any
29
29
  timeoutMs: any
30
+ disableThreading: boolean
30
31
 
31
32
  static workerRefs: any[] = []
32
33
 
33
34
  constructor(type: any, opts: any = { timeoutMs: null, count: 1 }) {
34
35
  this.type = type
35
36
  this.count = opts.count ? opts.count : 1
36
- this.disableThreading =
37
- env.isTest() ||
38
- env.DISABLE_THREADING ||
39
- this.count === 0 ||
40
- env.isInThread()
37
+ this.disableThreading = this.shouldDisableThreading()
41
38
  if (!this.disableThreading) {
42
39
  const workerOpts: any = {
43
40
  autoStart: true,
@@ -47,33 +44,44 @@ export class Thread {
47
44
  this.timeoutMs = opts.timeoutMs
48
45
  workerOpts.maxCallTime = opts.timeoutMs
49
46
  }
50
- this.workers = workerFarm(workerOpts, typeToFile(type))
47
+ this.workers = workerFarm(workerOpts, typeToFile(type), ["execute"])
51
48
  Thread.workerRefs.push(this.workers)
52
49
  }
53
50
  }
54
51
 
52
+ shouldDisableThreading(): boolean {
53
+ return !!(
54
+ env.isTest() ||
55
+ env.DISABLE_THREADING ||
56
+ this.count === 0 ||
57
+ env.isInThread()
58
+ )
59
+ }
60
+
55
61
  run(data: any) {
62
+ const timeout = this.timeoutMs
56
63
  return new Promise((resolve, reject) => {
57
- let fncToCall
64
+ function fire(worker: any) {
65
+ worker.execute(data, (err: any, response: any) => {
66
+ if (err && err.type === "TimeoutError") {
67
+ reject(
68
+ new Error(`Query response time exceeded ${timeout}ms timeout.`)
69
+ )
70
+ } else if (err) {
71
+ reject(err)
72
+ } else {
73
+ resolve(response)
74
+ }
75
+ })
76
+ }
58
77
  // if in test then don't use threading
59
78
  if (this.disableThreading) {
60
- fncToCall = require(typeToFile(this.type))
79
+ import(typeToFile(this.type)).then((thread: any) => {
80
+ fire(thread)
81
+ })
61
82
  } else {
62
- fncToCall = this.workers
83
+ fire(this.workers)
63
84
  }
64
- fncToCall(data, (err: any, response: any) => {
65
- if (err && err.type === "TimeoutError") {
66
- reject(
67
- new Error(
68
- `Query response time exceeded ${this.timeoutMs}ms timeout.`
69
- )
70
- )
71
- } else if (err) {
72
- reject(err)
73
- } else {
74
- resolve(response)
75
- }
76
- })
77
85
  })
78
86
  }
79
87
 
@@ -1,5 +1,6 @@
1
- const threadUtils = require("./utils")
1
+ import { default as threadUtils } from "./utils"
2
2
  threadUtils.threadSetup()
3
+ import { WorkerCallback, QueryEvent, QueryVariable } from "./definitions"
3
4
  const ScriptRunner = require("../utilities/scriptRunner")
4
5
  const { integrations } = require("../integrations")
5
6
  const { processStringSync } = require("@budibase/string-templates")
@@ -19,7 +20,22 @@ const {
19
20
  } = require("../integrations/queries/sql")
20
21
 
21
22
  class QueryRunner {
22
- constructor(input, flags = { noRecursiveQuery: false }) {
23
+ datasource: any
24
+ queryVerb: string
25
+ queryId: string
26
+ fields: any
27
+ parameters: any
28
+ pagination: any
29
+ transformer: any
30
+ cachedVariables: any[]
31
+ ctx: any
32
+ queryResponse: any
33
+ noRecursiveQuery: boolean
34
+ hasRerun: boolean
35
+ hasRefreshedOAuth: boolean
36
+ hasDynamicVariables: boolean
37
+
38
+ constructor(input: QueryEvent, flags = { noRecursiveQuery: false }) {
23
39
  this.datasource = input.datasource
24
40
  this.queryVerb = input.queryVerb
25
41
  this.fields = input.fields
@@ -37,9 +53,10 @@ class QueryRunner {
37
53
  this.queryResponse = {}
38
54
  this.hasRerun = false
39
55
  this.hasRefreshedOAuth = false
56
+ this.hasDynamicVariables = false
40
57
  }
41
58
 
42
- async execute() {
59
+ async execute(): Promise<any> {
43
60
  let { datasource, fields, queryVerb, transformer } = this
44
61
 
45
62
  let datasourceClone = cloneDeep(datasource)
@@ -52,7 +69,7 @@ class QueryRunner {
52
69
 
53
70
  if (datasourceClone.config.authConfigs) {
54
71
  datasourceClone.config.authConfigs =
55
- datasourceClone.config.authConfigs.map(config => {
72
+ datasourceClone.config.authConfigs.map((config: any) => {
56
73
  return enrichQueryFields(config, this.ctx)
57
74
  })
58
75
  }
@@ -138,8 +155,8 @@ class QueryRunner {
138
155
  }
139
156
 
140
157
  // map into JSON if just raw primitive here
141
- if (rows.find(row => typeof row !== "object")) {
142
- rows = rows.map(value => ({ value }))
158
+ if (rows.find((row: any) => typeof row !== "object")) {
159
+ rows = rows.map((value: any) => ({ value }))
143
160
  }
144
161
 
145
162
  // get all the potential fields in the schema
@@ -152,7 +169,7 @@ class QueryRunner {
152
169
  return { rows, keys, info, extra, pagination }
153
170
  }
154
171
 
155
- async runAnotherQuery(queryId, parameters) {
172
+ async runAnotherQuery(queryId: string, parameters: any) {
156
173
  const db = getAppDB()
157
174
  const query = await db.get(queryId)
158
175
  const datasource = await db.get(query.datasourceId)
@@ -163,12 +180,13 @@ class QueryRunner {
163
180
  fields: query.fields,
164
181
  parameters,
165
182
  transformer: query.transformer,
183
+ queryId,
166
184
  },
167
185
  { noRecursiveQuery: true }
168
186
  ).execute()
169
187
  }
170
188
 
171
- async refreshOAuth2(ctx) {
189
+ async refreshOAuth2(ctx: any) {
172
190
  const { oauth2, providerType, _id } = ctx.user
173
191
  const { configId } = ctx.auth
174
192
 
@@ -200,7 +218,7 @@ class QueryRunner {
200
218
  return resp
201
219
  }
202
220
 
203
- async getDynamicVariable(variable) {
221
+ async getDynamicVariable(variable: QueryVariable) {
204
222
  let { parameters } = this
205
223
  const queryId = variable.queryId,
206
224
  name = variable.name
@@ -233,7 +251,7 @@ class QueryRunner {
233
251
  if (!this.noRecursiveQuery) {
234
252
  // need to see if this uses any variables
235
253
  const stringFields = JSON.stringify(fields)
236
- const foundVars = dynamicVars.filter(variable => {
254
+ const foundVars = dynamicVars.filter((variable: QueryVariable) => {
237
255
  // don't allow a query to use its own dynamic variable (loop)
238
256
  if (variable.queryId === this.queryId) {
239
257
  return false
@@ -242,7 +260,9 @@ class QueryRunner {
242
260
  const regex = new RegExp(`{{[ ]*${variable.name}[ ]*}}`)
243
261
  return regex.test(stringFields)
244
262
  })
245
- const dynamics = foundVars.map(dynVar => this.getDynamicVariable(dynVar))
263
+ const dynamics = foundVars.map((dynVar: QueryVariable) =>
264
+ this.getDynamicVariable(dynVar)
265
+ )
246
266
  const responses = await Promise.all(dynamics)
247
267
  for (let i = 0; i < foundVars.length; i++) {
248
268
  const variable = foundVars[i]
@@ -264,7 +284,7 @@ class QueryRunner {
264
284
  }
265
285
  }
266
286
 
267
- module.exports = (input, callback) => {
287
+ export function execute(input: QueryEvent, callback: WorkerCallback) {
268
288
  doInAppContext(input.appId, async () => {
269
289
  const Runner = new QueryRunner(input)
270
290
  try {