diff --git a/packages/backend-core/src/db/constants.js b/packages/backend-core/src/db/constants.js
deleted file mode 100644
index 12626fb90e..0000000000
--- a/packages/backend-core/src/db/constants.js
+++ /dev/null
@@ -1,42 +0,0 @@
-exports.SEPARATOR = "_"
-exports.UNICODE_MAX = "\ufff0"
-
-const PRE_APP = "app"
-const PRE_DEV = "dev"
-
-exports.DocumentTypes = {
- USER: "us",
- WORKSPACE: "workspace",
- CONFIG: "config",
- TEMPLATE: "template",
- APP: PRE_APP,
- DEV: PRE_DEV,
- APP_DEV: `${PRE_APP}${exports.SEPARATOR}${PRE_DEV}`,
- APP_METADATA: `${PRE_APP}${exports.SEPARATOR}metadata`,
- ROLE: "role",
- MIGRATIONS: "migrations",
- DEV_INFO: "devinfo",
-}
-
-exports.StaticDatabases = {
- GLOBAL: {
- name: "global-db",
- docs: {
- apiKeys: "apikeys",
- usageQuota: "usage_quota",
- licenseInfo: "license_info",
- },
- },
- // contains information about tenancy and so on
- PLATFORM_INFO: {
- name: "global-info",
- docs: {
- tenants: "tenants",
- install: "install",
- },
- },
-}
-
-exports.APP_PREFIX = exports.DocumentTypes.APP + exports.SEPARATOR
-exports.APP_DEV = exports.APP_DEV_PREFIX =
- exports.DocumentTypes.APP_DEV + exports.SEPARATOR
diff --git a/packages/backend-core/src/db/constants.ts b/packages/backend-core/src/db/constants.ts
new file mode 100644
index 0000000000..be0e824e61
--- /dev/null
+++ b/packages/backend-core/src/db/constants.ts
@@ -0,0 +1,58 @@
+export const SEPARATOR = "_"
+export const UNICODE_MAX = "\ufff0"
+
+/**
+ * Can be used to create a few different forms of querying a view.
+ */
+export enum AutomationViewModes {
+ ALL = "all",
+ AUTOMATION = "automation",
+ STATUS = "status",
+}
+
+export enum ViewNames {
+ USER_BY_EMAIL = "by_email",
+ BY_API_KEY = "by_api_key",
+ USER_BY_BUILDERS = "by_builders",
+ LINK = "by_link",
+ ROUTING = "screen_routes",
+ AUTOMATION_LOGS = "automation_logs",
+}
+
+export enum DocumentTypes {
+ USER = "us",
+ WORKSPACE = "workspace",
+ CONFIG = "config",
+ TEMPLATE = "template",
+ APP = "app",
+ DEV = "dev",
+ APP_DEV = "app_dev",
+ APP_METADATA = "app_metadata",
+ ROLE = "role",
+ MIGRATIONS = "migrations",
+ DEV_INFO = "devinfo",
+ AUTOMATION_LOG = "log_au",
+}
+
+export const StaticDatabases = {
+ GLOBAL: {
+ name: "global-db",
+ docs: {
+ apiKeys: "apikeys",
+ usageQuota: "usage_quota",
+ licenseInfo: "license_info",
+ },
+ },
+ // contains information about tenancy and so on
+ PLATFORM_INFO: {
+ name: "global-info",
+ docs: {
+ tenants: "tenants",
+ install: "install",
+ },
+ },
+}
+
+export const APP_PREFIX = exports.DocumentTypes.APP + exports.SEPARATOR
+export const APP_DEV = exports.DocumentTypes.APP_DEV + exports.SEPARATOR
+export const APP_DEV_PREFIX = APP_DEV
diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts
index 54af4fc7a2..4910899565 100644
--- a/packages/backend-core/src/db/utils.ts
+++ b/packages/backend-core/src/db/utils.ts
@@ -1,7 +1,7 @@
import { newid } from "../hashing"
import { DEFAULT_TENANT_ID, Configs } from "../constants"
import env from "../environment"
-import { SEPARATOR, DocumentTypes, UNICODE_MAX } from "./constants"
+import { SEPARATOR, DocumentTypes, UNICODE_MAX, ViewNames } from "./constants"
import { getTenantId, getGlobalDBName, getGlobalDB } from "../tenancy"
import fetch from "node-fetch"
import { doWithDB, allDbs } from "./index"
@@ -12,12 +12,6 @@ import { isDevApp, isDevAppID } from "./conversions"
import { APP_PREFIX } from "./constants"
import * as events from "../events"
-export const ViewNames = {
- USER_BY_EMAIL: "by_email",
- BY_API_KEY: "by_api_key",
- USER_BY_BUILDERS: "by_builders",
-}
-
export * from "./constants"
export * from "./conversions"
export { default as Replication } from "./Replication"
@@ -61,6 +55,13 @@ export function getDocParams(
}
}
+/**
+ * Retrieve the correct index for a view based on default design DB.
+ */
+export function getQueryIndex(viewName: ViewNames) {
+ return `database/${viewName}`
+}
+
/**
* Generates a new workspace ID.
* @returns {string} The new workspace ID which the workspace doc can be stored under.
diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts
index 6eb6b14bc4..ab89eed3b2 100644
--- a/packages/backend-core/src/index.ts
+++ b/packages/backend-core/src/index.ts
@@ -13,6 +13,7 @@ import deprovisioning from "./context/deprovision"
import auth from "./auth"
import constants from "./constants"
import * as dbConstants from "./db/constants"
+import logging from "./logging"
// mimic the outer package exports
import * as db from "./pkg/db"
@@ -49,6 +50,7 @@ const core = {
deprovisioning,
installation,
errors,
+ logging,
...errorClasses,
}
diff --git a/packages/bbui/src/ActionButton/ActionButton.svelte b/packages/bbui/src/ActionButton/ActionButton.svelte
index b518ac3d92..2d23120046 100644
--- a/packages/bbui/src/ActionButton/ActionButton.svelte
+++ b/packages/bbui/src/ActionButton/ActionButton.svelte
@@ -13,6 +13,7 @@
export let size = "M"
export let active = false
export let fullWidth = false
+ export let noPadding = false
function longPress(element) {
if (!longPressable) return
@@ -41,6 +42,7 @@
class:spectrum-ActionButton--quiet={quiet}
class:spectrum-ActionButton--emphasized={emphasized}
class:is-selected={selected}
+ class:noPadding
class:fullWidth
class="spectrum-ActionButton spectrum-ActionButton--size{size}"
class:active
@@ -80,4 +82,8 @@
.active svg {
color: var(--spectrum-global-color-blue-600);
}
+ .noPadding {
+ padding: 0;
+ min-width: 0;
+ }
diff --git a/packages/bbui/src/Notification/Notification.svelte b/packages/bbui/src/Notification/Notification.svelte
index 1d21131553..53ab062701 100644
--- a/packages/bbui/src/Notification/Notification.svelte
+++ b/packages/bbui/src/Notification/Notification.svelte
@@ -1,15 +1,20 @@
-
+
{#if icon}
{/if}
-
+
{message || ""}
+ {#if action}
+
+ {actionMessage}
+
+ {/if}
{#if dismissable}
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 2c9e0ecb7e..c1b342f320 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -77,6 +77,7 @@
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
"codemirror": "^5.59.0",
+ "dayjs": "^1.11.2",
"downloadjs": "1.4.7",
"lodash": "4.17.21",
"posthog-js": "1.4.5",
diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js
index cf42492c05..dd09e3356a 100644
--- a/packages/builder/src/builderStore/store/automation/index.js
+++ b/packages/builder/src/builderStore/store/automation/index.js
@@ -5,6 +5,7 @@ import { cloneDeep } from "lodash/fp"
const initialAutomationState = {
automations: [],
+ showTestPanel: false,
blockDefinitions: {
TRIGGER: [],
ACTION: [],
@@ -19,6 +20,17 @@ export const getAutomationStore = () => {
}
const automationActions = store => ({
+ definitions: async () => {
+ const response = await API.getAutomationDefinitions()
+ store.update(state => {
+ state.blockDefinitions = {
+ TRIGGER: response.trigger,
+ ACTION: response.action,
+ }
+ return state
+ })
+ return response
+ },
fetch: async () => {
const responses = await Promise.all([
API.getAutomations(),
@@ -109,6 +121,20 @@ const automationActions = store => ({
return state
})
},
+ getLogs: async ({ automationId, startDate, status, page } = {}) => {
+ return await API.getAutomationLogs({
+ automationId,
+ startDate,
+ status,
+ page,
+ })
+ },
+ clearLogErrors: async ({ automationId, appId } = {}) => {
+ return await API.clearAutomationLogErrors({
+ automationId,
+ appId,
+ })
+ },
addTestDataToAutomation: data => {
store.update(state => {
state.selectedAutomation.addTestData(data)
@@ -117,11 +143,10 @@ const automationActions = store => ({
},
addBlockToAutomation: (block, blockIdx) => {
store.update(state => {
- const newBlock = state.selectedAutomation.addBlock(
+ state.selectedBlock = state.selectedAutomation.addBlock(
cloneDeep(block),
blockIdx
)
- state.selectedBlock = newBlock
return state
})
},
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
index 3e58b25ff6..9c987c89d8 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
@@ -65,7 +65,7 @@
{
- $automationStore.selectedAutomation.automation.showTestPanel = true
+ $automationStore.showTestPanel = true
}}
size="M">Test Details
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
index a4c41c6948..291575f3f2 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
@@ -1,6 +1,4 @@
@@ -60,16 +80,13 @@
- {#if showTestStatus && testResult && testResult[0]}
+ {#if showTestStatus && testResult}
{testResult[0].outputs?.success || isTrigger
- ? "Success"
- : "Error"}{status?.message}
{/if}
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
index fecd0fcc7e..b86cffb1f9 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte
@@ -51,7 +51,7 @@
$automationStore.selectedAutomation?.automation,
testData
)
- $automationStore.selectedAutomation.automation.showTestPanel = true
+ $automationStore.showTestPanel = true
} catch (error) {
notifications.error("Error testing notification")
}
diff --git a/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte b/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte
new file mode 100644
index 0000000000..a2eb904c94
--- /dev/null
+++ b/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte
@@ -0,0 +1,137 @@
+
+
+
+ {#each blocks as block, idx}
+
+ {#if block.stepId !== "LOOP"}
+
+ {#if showParameters && showParameters[block.id]}
+
+ {#if filteredResults?.[idx]?.outputs.iterations}
+
+
+
+
+
+
+ {/if}
+
+
+ {/if}
+ {/if}
+
+ {#if blocks.length - 1 !== idx}
+
+ {/if}
+ {/each}
+
+
+
diff --git a/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte b/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte
index 8c38d8a689..9e84350cd0 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/TestPanel.svelte
@@ -1,11 +1,11 @@
@@ -34,7 +36,7 @@
{
- $automationStore.selectedAutomation.automation.showTestPanel = false
+ $automationStore.showTestPanel = false
}}
hoverable
name="Close"
@@ -44,59 +46,9 @@
-
- {#each blocks as block, idx}
-
- {#if block.stepId !== "LOOP"}
-
- {#if showParameters && showParameters[block.id]}
-
- {#if testResults?.[idx]?.outputs.iterations}
-
-
-
-
-
-
- {/if}
-
-
- {/if}
- {/if}
-
- {#if blocks.length - 1 !== idx}
-
- {/if}
- {/each}
-
+
diff --git a/packages/builder/src/components/common/renderers/DateTimeRenderer.svelte b/packages/builder/src/components/common/renderers/DateTimeRenderer.svelte
new file mode 100644
index 0000000000..8bf9499b98
--- /dev/null
+++ b/packages/builder/src/components/common/renderers/DateTimeRenderer.svelte
@@ -0,0 +1,6 @@
+
+
+{new dayjs(value).format("MMM D, YYYY HH:mm")}
diff --git a/packages/builder/src/components/deploy/DeployNavigation.svelte b/packages/builder/src/components/deploy/DeployNavigation.svelte
index 9cb7efd362..d12b31beaf 100644
--- a/packages/builder/src/components/deploy/DeployNavigation.svelte
+++ b/packages/builder/src/components/deploy/DeployNavigation.svelte
@@ -52,7 +52,7 @@
reviewPendingDeployments(deployments, newDeployments)
return newDeployments
} catch (err) {
- notifications.error("Error fetching deployment history")
+ notifications.error("Error fetching deployment overview")
}
}
diff --git a/packages/builder/src/components/deploy/DeploymentHistory.svelte b/packages/builder/src/components/deploy/DeploymentHistory.svelte
index eb5c8953cc..e025abf1c7 100644
--- a/packages/builder/src/components/deploy/DeploymentHistory.svelte
+++ b/packages/builder/src/components/deploy/DeploymentHistory.svelte
@@ -55,7 +55,7 @@
deployments = newDeployments
} catch (err) {
clearInterval(poll)
- notifications.error("Error fetching deployment history")
+ notifications.error("Error fetching deployment overview")
}
}
diff --git a/packages/builder/src/components/portal/overview/automation/HistoryDetailsPanel.svelte b/packages/builder/src/components/portal/overview/automation/HistoryDetailsPanel.svelte
new file mode 100644
index 0000000000..76e2095e64
--- /dev/null
+++ b/packages/builder/src/components/portal/overview/automation/HistoryDetailsPanel.svelte
@@ -0,0 +1,91 @@
+
+
+{#if history}
+
+
+
+
+
+
+
+
+
+
{history.automationName}
+
+
+ {#if exists}
+
+ $goto(`../../../app/${appId}/automate/${history.automationId}`)}
+ >Edit automation
+ {/if}
+
+
+
+ {#key history}
+
+ {/key}
+
+
+{:else}
+ No details found
+{/if}
+
+
diff --git a/packages/builder/src/components/portal/overview/automation/HistoryTab.svelte b/packages/builder/src/components/portal/overview/automation/HistoryTab.svelte
new file mode 100644
index 0000000000..9c752965e8
--- /dev/null
+++ b/packages/builder/src/components/portal/overview/automation/HistoryTab.svelte
@@ -0,0 +1,218 @@
+
+
+
+
+
+ {#if runHistory}
+
+ {/if}
+
+
+ {
+ showPanel = false
+ }}
+ />
+
+
+
+
+
diff --git a/packages/builder/src/components/portal/overview/automation/StatusRenderer.svelte b/packages/builder/src/components/portal/overview/automation/StatusRenderer.svelte
new file mode 100644
index 0000000000..5f71041809
--- /dev/null
+++ b/packages/builder/src/components/portal/overview/automation/StatusRenderer.svelte
@@ -0,0 +1,38 @@
+
+
+
+
+
+ {status.message}
+
+
+
+
diff --git a/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
index a713067bbe..b1d01d58cf 100644
--- a/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
@@ -5,6 +5,7 @@
import CreateAutomationModal from "components/automation/AutomationPanel/CreateAutomationModal.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
import TestPanel from "components/automation/AutomationBuilder/TestPanel.svelte"
+ import { onMount } from "svelte"
$: automation =
$automationStore.selectedAutomation?.automation ||
@@ -12,6 +13,9 @@
let modal
let webhookModal
+ onMount(() => {
+ $automationStore.showTestPanel = false
+ })
@@ -45,7 +49,7 @@
{/if}
- {#if automation?.showTestPanel}
+ {#if $automationStore.showTestPanel}
diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte
index 3aee5d728d..de5ad178cb 100644
--- a/packages/builder/src/pages/builder/portal/apps/index.svelte
+++ b/packages/builder/src/pages/builder/portal/apps/index.svelte
@@ -7,6 +7,7 @@
Modal,
Page,
notifications,
+ Notification,
Body,
Search,
} from "@budibase/bbui"
@@ -37,6 +38,7 @@
let searchTerm = ""
let cloud = $admin.cloud
let creatingFromTemplate = false
+ let automationErrors
const resolveWelcomeMessage = (auth, apps) => {
const userWelcome = auth?.user?.firstName
@@ -59,7 +61,8 @@
)
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
- $: unlocked = lockedApps?.length == 0
+ $: unlocked = lockedApps?.length === 0
+ $: automationErrors = getAutomationErrors(enrichedApps)
const enrichApps = (apps, user, sortBy) => {
const enrichedApps = apps.map(app => ({
@@ -89,6 +92,36 @@
}
}
+ const getAutomationErrors = apps => {
+ const automationErrors = {}
+ for (let app of apps) {
+ if (app.automationErrors) {
+ if (errorCount(app.automationErrors) > 0) {
+ automationErrors[app.devId] = app.automationErrors
+ }
+ }
+ }
+ return automationErrors
+ }
+
+ const goToAutomationError = appId => {
+ const params = new URLSearchParams({
+ tab: "Automation History",
+ open: "error",
+ })
+ $goto(`../overview/${appId}?${params.toString()}`)
+ }
+
+ const errorCount = errors => {
+ return Object.values(errors).reduce((acc, next) => acc + next.length, 0)
+ }
+
+ const automationErrorMessage = appId => {
+ const app = enrichedApps.find(app => app.devId === appId)
+ const errors = automationErrors[appId]
+ return `${app.name} - Automation error (${errorCount(errors)})`
+ }
+
const initiateAppCreation = () => {
if ($apps?.length) {
$goto("/builder/portal/apps/create")
@@ -208,6 +241,23 @@
{#if loaded}
+ {#each Object.keys(automationErrors || {}) as appId}
+ goToAutomationError(appId)}
+ type="error"
+ icon="Alert"
+ actionMessage={errorCount(automationErrors[appId]) > 1
+ ? "View errors"
+ : "View error"}
+ on:dismiss={async () => {
+ await automationStore.actions.clearLogErrors({ appId })
+ await apps.load()
+ }}
+ message={automationErrorMessage(appId)}
+ />
+ {/each}
diff --git a/packages/builder/src/pages/builder/portal/manage/users/index.svelte b/packages/builder/src/pages/builder/portal/manage/users/index.svelte
index 0da8c1345a..5a5f6c987a 100644
--- a/packages/builder/src/pages/builder/portal/manage/users/index.svelte
+++ b/packages/builder/src/pages/builder/portal/manage/users/index.svelte
@@ -27,13 +27,23 @@
}
let pageInfo = createPaginationStore()
- let search = undefined
+ let prevSearch = undefined,
+ search = undefined
$: page = $pageInfo.page
$: fetchUsers(page, search)
let createUserModal
async function fetchUsers(page, search) {
+ if ($pageInfo.loading) {
+ return
+ }
+ // need to remove the page if they've started searching
+ if (search && !prevSearch) {
+ pageInfo.reset()
+ page = undefined
+ }
+ prevSearch = search
try {
pageInfo.loading()
await users.search({ page, search })
diff --git a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte
index 86e6511ae0..2d346dff7d 100644
--- a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte
+++ b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte
@@ -27,6 +27,7 @@
import AppLockModal from "components/common/AppLockModal.svelte"
import EditableIcon from "components/common/EditableIcon.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
+ import HistoryTab from "components/portal/overview/automation/HistoryTab.svelte"
import { checkIncomingDeploymentStatus } from "components/deploy/utils"
import { onDestroy, onMount } from "svelte"
@@ -187,6 +188,10 @@
})
onMount(async () => {
+ const params = new URLSearchParams(window.location.search)
+ if (params.get("tab")) {
+ selectedTab = params.get("tab")
+ }
try {
if (!apps.length) {
await apps.load()
@@ -211,7 +216,7 @@
{:then _}
-
+