Merge pull request #14218 from Budibase/BUDI-8430/prevent-edits-and-deletions
Prevent edits and deletions for row action automations
This commit is contained in:
commit
421e42d8fa
|
@ -6,6 +6,7 @@
|
|||
contextMenuStore,
|
||||
} from "stores/builder"
|
||||
import { notifications, Icon } from "@budibase/bbui"
|
||||
import { sdk } from "@budibase/shared-core"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import UpdateAutomationModal from "components/automation/AutomationPanel/UpdateAutomationModal.svelte"
|
||||
import NavItem from "components/common/NavItem.svelte"
|
||||
|
@ -35,7 +36,11 @@
|
|||
}
|
||||
|
||||
const getContextMenuItems = () => {
|
||||
return [
|
||||
const isRowAction = sdk.automations.isRowAction(automation)
|
||||
const result = []
|
||||
if (!isRowAction) {
|
||||
result.push(
|
||||
...[
|
||||
{
|
||||
icon: "Delete",
|
||||
name: "Delete",
|
||||
|
@ -60,7 +65,11 @@
|
|||
disabled: automation.definition.trigger.name === "Webhook",
|
||||
callback: duplicateAutomation,
|
||||
},
|
||||
{
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
result.push({
|
||||
icon: automation.disabled ? "CheckmarkCircle" : "Cancel",
|
||||
name: automation.disabled ? "Activate" : "Pause",
|
||||
keyBind: null,
|
||||
|
@ -72,8 +81,8 @@
|
|||
automation.disabled
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const openContextMenu = e => {
|
||||
|
@ -89,7 +98,7 @@
|
|||
on:contextmenu={openContextMenu}
|
||||
{icon}
|
||||
iconColor={"var(--spectrum-global-color-gray-900)"}
|
||||
text={automation.name}
|
||||
text={automation.displayName}
|
||||
selected={automation._id === $selectedAutomation?._id}
|
||||
hovering={automation._id === $contextMenuStore.id}
|
||||
on:click={() => automationStore.actions.select(automation._id)}
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
})
|
||||
.map(automation => ({
|
||||
...automation,
|
||||
name:
|
||||
displayName:
|
||||
$automationStore.automationDisplayData[automation._id].displayName ||
|
||||
automation.name,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const lowerA = a.name.toLowerCase()
|
||||
const lowerB = b.name.toLowerCase()
|
||||
const lowerA = a.displayName.toLowerCase()
|
||||
const lowerB = b.displayName.toLowerCase()
|
||||
return lowerA > lowerB ? 1 : -1
|
||||
})
|
||||
|
||||
|
|
|
@ -104,19 +104,8 @@ const automationActions = store => ({
|
|||
},
|
||||
save: async automation => {
|
||||
const response = await API.updateAutomation(automation)
|
||||
store.update(state => {
|
||||
const updatedAutomation = response.automation
|
||||
const existingIdx = state.automations.findIndex(
|
||||
existing => existing._id === automation._id
|
||||
)
|
||||
if (existingIdx !== -1) {
|
||||
state.automations.splice(existingIdx, 1, updatedAutomation)
|
||||
return state
|
||||
} else {
|
||||
state.automations = [...state.automations, updatedAutomation]
|
||||
}
|
||||
return state
|
||||
})
|
||||
|
||||
await store.actions.fetch()
|
||||
return response.automation
|
||||
},
|
||||
delete: async automation => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as triggers from "../../automations/triggers"
|
||||
import { sdk as coreSdk } from "@budibase/shared-core"
|
||||
import { DocumentType } from "../../db/utils"
|
||||
import { updateTestHistory, removeDeprecated } from "../../automations/utils"
|
||||
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
||||
|
@ -94,6 +95,11 @@ export async function find(ctx: UserCtx) {
|
|||
export async function destroy(ctx: UserCtx<void, DeleteAutomationResponse>) {
|
||||
const automationId = ctx.params.id
|
||||
|
||||
const automation = await sdk.automations.get(ctx.params.id)
|
||||
if (coreSdk.automations.isRowAction(automation)) {
|
||||
ctx.throw("Row actions automations cannot be deleted", 422)
|
||||
}
|
||||
|
||||
ctx.body = await sdk.automations.remove(automationId, ctx.params.rev)
|
||||
builderSocket?.emitAutomationDeletion(ctx, automationId)
|
||||
}
|
||||
|
|
|
@ -425,6 +425,22 @@ describe("/automations", () => {
|
|||
expect(events.automation.deleted).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("cannot delete a row action automation", async () => {
|
||||
const automation = await config.createAutomation(
|
||||
setup.structures.rowActionAutomation()
|
||||
)
|
||||
await request
|
||||
.delete(`/api/automations/${automation._id}/${automation._rev}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(422, {
|
||||
message: "Row actions automations cannot be deleted",
|
||||
status: 422,
|
||||
})
|
||||
|
||||
expect(events.automation.deleted).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
const automation = await config.createAutomation()
|
||||
await checkBuilderEndpoint({
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Automation, Webhook, WebhookActionType } from "@budibase/types"
|
||||
import { sdk } from "@budibase/shared-core"
|
||||
import { generateAutomationID, getAutomationParams } from "../../../db/utils"
|
||||
import { deleteEntityMetadata } from "../../../utilities"
|
||||
import { MetadataTypes } from "../../../constants"
|
||||
|
@ -117,7 +118,6 @@ export async function create(automation: Automation) {
|
|||
|
||||
export async function update(automation: Automation) {
|
||||
automation = { ...automation }
|
||||
|
||||
if (!automation._id || !automation._rev) {
|
||||
throw new HTTPError("_id or _rev fields missing", 400)
|
||||
}
|
||||
|
@ -281,4 +281,11 @@ function guardInvalidUpdatesAndThrow(
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
sdk.automations.isRowAction(automation) &&
|
||||
automation.name !== oldAutomation.name
|
||||
) {
|
||||
throw new Error("Row actions cannot be renamed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { sample } from "lodash/fp"
|
||||
import { Automation } from "@budibase/types"
|
||||
import { Automation, AutomationTriggerStepId } from "@budibase/types"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
import automationSdk from "../"
|
||||
import { structures } from "../../../../api/routes/tests/utilities"
|
||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||
|
@ -12,6 +13,38 @@ describe("automation sdk", () => {
|
|||
})
|
||||
|
||||
describe("update", () => {
|
||||
it("can rename existing automations", async () => {
|
||||
await config.doInContext(config.getAppId(), async () => {
|
||||
const automation = structures.newAutomation()
|
||||
|
||||
const response = await automationSdk.create(automation)
|
||||
|
||||
const newName = generator.guid()
|
||||
const update = { ...response, name: newName }
|
||||
const result = await automationSdk.update(update)
|
||||
expect(result.name).toEqual(newName)
|
||||
})
|
||||
})
|
||||
|
||||
it("cannot rename row action automations", async () => {
|
||||
await config.doInContext(config.getAppId(), async () => {
|
||||
const automation = structures.newAutomation({
|
||||
trigger: {
|
||||
...structures.automationTrigger(),
|
||||
stepId: AutomationTriggerStepId.ROW_ACTION,
|
||||
},
|
||||
})
|
||||
|
||||
const response = await automationSdk.create(automation)
|
||||
|
||||
const newName = generator.guid()
|
||||
const update = { ...response, name: newName }
|
||||
await expect(automationSdk.update(update)).rejects.toThrow(
|
||||
"Row actions cannot be renamed"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
["trigger", (a: Automation) => a.definition.trigger],
|
||||
["step", (a: Automation) => a.definition.steps[0]],
|
||||
|
|
|
@ -2,9 +2,9 @@ import {
|
|||
Automation,
|
||||
AutomationActionStepId,
|
||||
AutomationBuilderData,
|
||||
AutomationTriggerStepId,
|
||||
TableRowActions,
|
||||
} from "@budibase/types"
|
||||
import { sdk as coreSdk } from "@budibase/shared-core"
|
||||
|
||||
export function checkForCollectStep(automation: Automation) {
|
||||
return automation.definition.steps.some(
|
||||
|
@ -39,14 +39,13 @@ export async function getBuilderData(
|
|||
|
||||
const result: Record<string, AutomationBuilderData> = {}
|
||||
for (const automation of automations) {
|
||||
const { trigger } = automation.definition
|
||||
const isRowAction = trigger.stepId === AutomationTriggerStepId.ROW_ACTION
|
||||
const isRowAction = coreSdk.automations.isRowAction(automation)
|
||||
if (!isRowAction) {
|
||||
result[automation._id!] = { displayName: automation.name }
|
||||
continue
|
||||
}
|
||||
|
||||
const { tableId, rowActionId } = trigger.inputs
|
||||
const { tableId, rowActionId } = automation.definition.trigger.inputs
|
||||
|
||||
const tableName = await getTableName(tableId)
|
||||
|
||||
|
|
|
@ -158,7 +158,10 @@ export function automationTrigger(
|
|||
}
|
||||
}
|
||||
|
||||
export function newAutomation({ steps, trigger }: any = {}) {
|
||||
export function newAutomation({
|
||||
steps,
|
||||
trigger,
|
||||
}: { steps?: AutomationStep[]; trigger?: AutomationTrigger } = {}) {
|
||||
const automation = basicAutomation()
|
||||
|
||||
if (trigger) {
|
||||
|
@ -176,6 +179,16 @@ export function newAutomation({ steps, trigger }: any = {}) {
|
|||
return automation
|
||||
}
|
||||
|
||||
export function rowActionAutomation() {
|
||||
const automation = newAutomation({
|
||||
trigger: {
|
||||
...automationTrigger(),
|
||||
stepId: AutomationTriggerStepId.ROW_ACTION,
|
||||
},
|
||||
})
|
||||
return automation
|
||||
}
|
||||
|
||||
export function basicAutomation(appId?: string): Automation {
|
||||
return {
|
||||
name: "My Automation",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { Automation, AutomationTriggerStepId } from "@budibase/types"
|
||||
|
||||
export function isRowAction(automation: Automation) {
|
||||
const result =
|
||||
automation.definition.trigger.stepId === AutomationTriggerStepId.ROW_ACTION
|
||||
return result
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
export * as applications from "./applications"
|
||||
export * as automations from "./automations"
|
||||
export * as users from "./users"
|
||||
|
|
Loading…
Reference in New Issue