Merge branch 'master' into fix/budi-7916
This commit is contained in:
commit
1636677ce9
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.15.3",
|
"version": "2.15.5",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -184,8 +184,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(idx === 0 && automation.trigger?.event === "row:update") ||
|
idx === 0 &&
|
||||||
automation.trigger?.event === "row:save"
|
(automation.trigger?.event === "row:update" ||
|
||||||
|
automation.trigger?.event === "row:save")
|
||||||
) {
|
) {
|
||||||
if (name !== "id" && name !== "revision") return `trigger.row.${name}`
|
if (name !== "id" && name !== "revision") return `trigger.row.${name}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
<Content showMobileNav>
|
<Content showMobileNav>
|
||||||
<SideNav slot="side-nav">
|
<SideNav slot="side-nav">
|
||||||
<SideNavItem
|
<SideNavItem
|
||||||
text="Automation History"
|
text="Automations"
|
||||||
url={$url("./automation-history")}
|
url={$url("./automations")}
|
||||||
active={$isActive("./automation-history")}
|
active={$isActive("./automations")}
|
||||||
/>
|
/>
|
||||||
<SideNavItem
|
<SideNavItem
|
||||||
text="Backups"
|
text="Backups"
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
Body,
|
Body,
|
||||||
Heading,
|
Heading,
|
||||||
Divider,
|
Divider,
|
||||||
|
Toggle,
|
||||||
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
|
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
|
||||||
import StatusRenderer from "./_components/StatusRenderer.svelte"
|
import StatusRenderer from "./_components/StatusRenderer.svelte"
|
||||||
|
@ -16,7 +18,7 @@
|
||||||
import { createPaginationStore } from "helpers/pagination"
|
import { createPaginationStore } from "helpers/pagination"
|
||||||
import { getContext, onDestroy, onMount } from "svelte"
|
import { getContext, onDestroy, onMount } from "svelte"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import { auth, licensing, admin } from "stores/portal"
|
import { auth, licensing, admin, apps } from "stores/portal"
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants } from "@budibase/frontend-core"
|
||||||
import Portal from "svelte-portal"
|
import Portal from "svelte-portal"
|
||||||
|
|
||||||
|
@ -35,9 +37,13 @@
|
||||||
let timeRange = null
|
let timeRange = null
|
||||||
let loaded = false
|
let loaded = false
|
||||||
|
|
||||||
|
$: app = $apps.find(app => app.devId === $store.appId?.includes(app.appId))
|
||||||
$: licensePlan = $auth.user?.license?.plan
|
$: licensePlan = $auth.user?.license?.plan
|
||||||
$: page = $pageInfo.page
|
$: page = $pageInfo.page
|
||||||
$: fetchLogs(automationId, status, page, timeRange)
|
$: fetchLogs(automationId, status, page, timeRange)
|
||||||
|
$: isCloud = $admin.cloud
|
||||||
|
|
||||||
|
$: chainAutomations = app?.automations?.chainAutomations ?? !isCloud
|
||||||
|
|
||||||
const timeOptions = [
|
const timeOptions = [
|
||||||
{ value: "90-d", label: "Past 90 days" },
|
{ value: "90-d", label: "Past 90 days" },
|
||||||
|
@ -124,6 +130,18 @@
|
||||||
sidePanel.open()
|
sidePanel.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function save({ detail }) {
|
||||||
|
try {
|
||||||
|
await apps.update($store.appId, {
|
||||||
|
automations: {
|
||||||
|
chainAutomations: detail,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error updating automation chaining setting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await automationStore.actions.fetch()
|
await automationStore.actions.fetch()
|
||||||
const params = new URLSearchParams(window.location.search)
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
@ -150,11 +168,30 @@
|
||||||
|
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading>Automation History</Heading>
|
<Heading>Automations</Heading>
|
||||||
<Body>View the automations your app has executed</Body>
|
<Body size="S">See your automation history and edit advanced settings</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="XS">Chain automations</Heading>
|
||||||
|
<Body size="S">Allow automations to trigger from other automations</Body>
|
||||||
|
<div class="setting-spacing">
|
||||||
|
<Toggle
|
||||||
|
text={"Enable chaining"}
|
||||||
|
on:change={e => {
|
||||||
|
save(e)
|
||||||
|
}}
|
||||||
|
value={chainAutomations}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="XS">History</Heading>
|
||||||
|
<Body size="S">Free plan stores up to 1 day of automation history</Body>
|
||||||
|
</Layout>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
|
@ -237,6 +274,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.setting-spacing {
|
||||||
|
padding-top: var(--spacing-s);
|
||||||
|
}
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { redirect } from "@roxi/routify"
|
import { redirect } from "@roxi/routify"
|
||||||
|
|
||||||
$redirect("../settings/automation-history")
|
$redirect("../settings/automations")
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
import { rowEmission, tableEmission } from "./utils"
|
import { rowEmission, tableEmission } from "./utils"
|
||||||
import mainEmitter from "./index"
|
import mainEmitter from "./index"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { Table, Row } from "@budibase/types"
|
import { Table, Row, DocumentType, App } from "@budibase/types"
|
||||||
|
import { context } from "@budibase/backend-core"
|
||||||
|
|
||||||
// max number of automations that can chain on top of each other
|
const MAX_AUTOMATIONS_ALLOWED = 5
|
||||||
// TODO: in future make this configurable at the automation level
|
|
||||||
const MAX_AUTOMATION_CHAIN = env.SELF_HOSTED ? 5 : 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special emitter which takes the count of automation runs which have occurred and blocks an
|
|
||||||
* automation from running if it has reached the maximum number of chained automations runs.
|
|
||||||
* This essentially "fakes" the normal emitter to add some functionality in-between to stop automations
|
|
||||||
* from getting stuck endlessly chaining.
|
|
||||||
*/
|
|
||||||
class AutomationEmitter {
|
class AutomationEmitter {
|
||||||
chainCount: number
|
chainCount: number
|
||||||
metadata: { automationChainCount: number }
|
metadata: { automationChainCount: number }
|
||||||
|
@ -24,7 +17,23 @@ class AutomationEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitRow(eventName: string, appId: string, row: Row, table?: Table) {
|
async getMaxAutomationChain() {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
const appMetadata = await db.get<App>(DocumentType.APP_METADATA)
|
||||||
|
let chainAutomations = appMetadata?.automations?.chainAutomations
|
||||||
|
|
||||||
|
if (chainAutomations === true) {
|
||||||
|
return MAX_AUTOMATIONS_ALLOWED
|
||||||
|
} else if (chainAutomations === undefined && env.SELF_HOSTED) {
|
||||||
|
return MAX_AUTOMATIONS_ALLOWED
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async emitRow(eventName: string, appId: string, row: Row, table?: Table) {
|
||||||
|
let MAX_AUTOMATION_CHAIN = await this.getMaxAutomationChain()
|
||||||
|
|
||||||
// don't emit even if we've reached max automation chain
|
// don't emit even if we've reached max automation chain
|
||||||
if (this.chainCount >= MAX_AUTOMATION_CHAIN) {
|
if (this.chainCount >= MAX_AUTOMATION_CHAIN) {
|
||||||
return
|
return
|
||||||
|
@ -39,9 +48,11 @@ class AutomationEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
emitTable(eventName: string, appId: string, table?: Table) {
|
async emitTable(eventName: string, appId: string, table?: Table) {
|
||||||
|
let MAX_AUTOMATION_CHAIN = await this.getMaxAutomationChain()
|
||||||
|
|
||||||
// don't emit even if we've reached max automation chain
|
// don't emit even if we've reached max automation chain
|
||||||
if (this.chainCount > MAX_AUTOMATION_CHAIN) {
|
if (this.chainCount >= MAX_AUTOMATION_CHAIN) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ async function checkResponse(
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
error = await response.json()
|
error = await response.json()
|
||||||
|
if (!error.message) {
|
||||||
|
error = JSON.stringify(error)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = await response.text()
|
error = await response.text()
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ export interface App extends Document {
|
||||||
automationErrors?: AppMetadataErrors
|
automationErrors?: AppMetadataErrors
|
||||||
icon?: AppIcon
|
icon?: AppIcon
|
||||||
features?: AppFeatures
|
features?: AppFeatures
|
||||||
|
automations?: AutomationSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstance {
|
export interface AppInstance {
|
||||||
|
@ -68,3 +69,7 @@ export interface AppFeatures {
|
||||||
componentValidation?: boolean
|
componentValidation?: boolean
|
||||||
disableUserMetadata?: boolean
|
disableUserMetadata?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AutomationSettings {
|
||||||
|
chainAutomations?: boolean
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue