diff --git a/packages/frontend-core/src/api/automations.js b/packages/frontend-core/src/api/automations.js
index ebb7c5cf56..c0b770a1c2 100644
--- a/packages/frontend-core/src/api/automations.js
+++ b/packages/frontend-core/src/api/automations.js
@@ -78,6 +78,8 @@ export const buildAutomationEndpoints = API => ({
* Get the logs for the app, or by automation ID.
* @param automationId The ID of the automation to get logs for.
* @param startDate An ISO date string to state the start of the date range.
+ * @param status The status, error or success.
+ * @param page The page to retrieve.
*/
getAutomationLogs: async ({ automationId, startDate, status, page }) => {
return await API.post({
@@ -90,4 +92,20 @@ export const buildAutomationEndpoints = API => ({
},
})
},
+
+ /**
+ * Clears automation log errors (which are creating notification) for
+ * automation or the app.
+ * @param automationId optional - the ID of the automation to clear errors for.
+ * @param appId The app ID to clear errors for.
+ */
+ clearAutomationLogErrors: async ({ automationId, appId }) => {
+ return await API.delete({
+ url: "/api/automations/logs",
+ body: {
+ appId,
+ automationId,
+ },
+ })
+ },
})
diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js
index 9bcbfc1146..4224ff5b5b 100644
--- a/packages/server/src/api/controllers/automation.js
+++ b/packages/server/src/api/controllers/automation.js
@@ -1,7 +1,11 @@
const actions = require("../../automations/actions")
const triggers = require("../../automations/triggers")
const { getLogs, oneDayAgo } = require("../../automations/logging")
-const { getAutomationParams, generateAutomationID } = require("../../db/utils")
+const {
+ getAutomationParams,
+ generateAutomationID,
+ DocumentTypes,
+} = require("../../db/utils")
const {
checkForWebhooks,
updateTestHistory,
@@ -10,8 +14,13 @@ const {
const { deleteEntityMetadata } = require("../../utilities")
const { MetadataTypes } = require("../../constants")
const { setTestFlag, clearTestFlag } = require("../../utilities/redis")
-const { getAppDB } = require("@budibase/backend-core/context")
+const {
+ getAppDB,
+ getProdAppDB,
+ doInAppContext,
+} = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
+const { app } = require("@budibase/backend-core/cache")
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
@@ -192,6 +201,25 @@ exports.logSearch = async function (ctx) {
ctx.body = await getLogs(startDate, status, automationId, page)
}
+exports.clearLogError = async function (ctx) {
+ const { automationId, appId } = ctx.request.body
+ await doInAppContext(appId, async () => {
+ const db = getProdAppDB()
+ const metadata = await db.get(DocumentTypes.APP_METADATA)
+ if (!automationId) {
+ delete metadata.automationErrors
+ } else if (
+ metadata.automationErrors &&
+ metadata.automationErrors[automationId]
+ ) {
+ delete metadata.automationErrors[automationId]
+ }
+ await db.put(metadata)
+ await app.invalidateAppMetadata(metadata.appId, metadata)
+ ctx.body = { message: `Error logs cleared.` }
+ })
+}
+
exports.getActionList = async function (ctx) {
ctx.body = ACTION_DEFS
}
diff --git a/packages/server/src/api/routes/automation.js b/packages/server/src/api/routes/automation.js
index d25f7bc328..e0f4744e1e 100644
--- a/packages/server/src/api/routes/automation.js
+++ b/packages/server/src/api/routes/automation.js
@@ -51,17 +51,22 @@ router
automationValidator(false),
controller.create
)
+ .post(
+ "/api/automations/logs/search",
+ authorized(BUILDER),
+ controller.logSearch
+ )
+ .delete(
+ "/api/automations/logs",
+ authorized(BUILDER),
+ controller.clearLogError
+ )
.delete(
"/api/automations/:id/:rev",
paramResource("id"),
authorized(BUILDER),
controller.destroy
)
- .post(
- "/api/automations/logs/search",
- authorized(BUILDER),
- controller.logSearch
- )
.post(
"/api/automations/:id/trigger",
appInfoMiddleware({ appType: AppType.PROD }),
diff --git a/packages/server/src/automations/logging/index.ts b/packages/server/src/automations/logging/index.ts
index 5ff46b007d..e6b851675f 100644
--- a/packages/server/src/automations/logging/index.ts
+++ b/packages/server/src/automations/logging/index.ts
@@ -69,13 +69,10 @@ async function clearOldHistory() {
const status = parts[parts.length - 1]
return status === AutomationStatus.ERROR
})
- .map((doc: any) => {
- const parts = doc.id.split(SEPARATOR)
- return `${parts[parts.length - 3]}${SEPARATOR}${parts[parts.length - 2]}`
- })
+ .map((doc: any) => doc.id)
await db.bulkDocs(toDelete)
if (errorLogIds.length) {
- await updateAppMetadataWithErrors(errorLogIds)
+ await updateAppMetadataWithErrors(errorLogIds, { clearing: true })
}
}
@@ -150,25 +147,34 @@ async function getLogsByView(
}
async function updateAppMetadataWithErrors(
- automationIds: string[],
+ logIds: string[],
{ clearing } = { clearing: false }
) {
const db = getProdAppDB()
// this will try multiple times with a delay between to update the metadata
await backOff(async () => {
const metadata = await db.get(DocumentTypes.APP_METADATA)
- for (let automationId of automationIds) {
+ for (let logId of logIds) {
+ const parts = logId.split(SEPARATOR)
+ const autoId = `${parts[parts.length - 3]}${SEPARATOR}${
+ parts[parts.length - 2]
+ }`
let errors: MetadataErrors = {}
if (metadata.automationErrors) {
errors = metadata.automationErrors as MetadataErrors
}
- const change = clearing ? -1 : 1
- errors[automationId] = errors[automationId]
- ? errors[automationId] + change
- : 1
+ if (!Array.isArray(errors[autoId])) {
+ errors[autoId] = []
+ }
+ const idx = errors[autoId].indexOf(logId)
+ if (clearing && idx !== -1) {
+ errors[autoId].splice(idx, 1)
+ } else {
+ errors[autoId].push(logId)
+ }
// if clearing and reach zero, this will pass and will remove the element
- if (!errors[automationId]) {
- delete errors[automationId]
+ if (errors[autoId].length === 0) {
+ delete errors[autoId]
}
metadata.automationErrors = errors
}
@@ -204,7 +210,7 @@ export async function storeLog(
// need to note on the app metadata that there is an error, store what the error is
if (status === AutomationStatus.ERROR) {
- await updateAppMetadataWithErrors([automation._id as string])
+ await updateAppMetadataWithErrors([id])
}
// clear up old logging for app
diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js
index 45220ff86e..10c1c98f38 100644
--- a/packages/server/src/db/utils.js
+++ b/packages/server/src/db/utils.js
@@ -396,8 +396,9 @@ exports.getAutomationLogParams = (
}
return {
...otherProps,
- startkey: `${base}${startDate}`,
- endkey: `${base}${endDate}${UNICODE_MAX}`,
+ descending: true,
+ startkey: `${base}${endDate}${UNICODE_MAX}`,
+ endkey: `${base}${startDate}`,
}
}
diff --git a/packages/server/src/definitions/common.ts b/packages/server/src/definitions/common.ts
index 7a7b0a70f8..c7573b4cc2 100644
--- a/packages/server/src/definitions/common.ts
+++ b/packages/server/src/definitions/common.ts
@@ -105,4 +105,4 @@ export interface Automation extends Base {
}
}
-export type MetadataErrors = { [key: string]: number }
+export type MetadataErrors = { [key: string]: string[] }