Skip to content

Commit

Permalink
feat: add the option to disable workflow (#1766)
Browse files Browse the repository at this point in the history
Co-authored-by: Tal <[email protected]>
Co-authored-by: Matvey Kukuy <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent f9cd4cf commit b23778b
Show file tree
Hide file tree
Showing 20 changed files with 389 additions and 86 deletions.
1 change: 1 addition & 0 deletions docs/workflows/syntax/basic-syntax.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ workflow:
workflow:
id: raw-sql-query
description: Monitor that time difference is no more than 1 hour
disabled: Optionally prevent this workflow from running
steps:
-
actions:
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/app/workflows/builder/builder-validators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function globalValidatorV2(

if (
!!definition?.properties &&
!definition.properties['manual'] &&
!definition.properties['manual'] &&
!definition.properties['interval'] &&
!definition.properties['alert']
) {
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/app/workflows/builder/builder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function Builder({
triggers = { alert: { source: alertSource, name: alertName } };
}
setDefinition(
wrapDefinitionV2({...generateWorkflow(alertUuid, "", "", [], [], triggers), isValid: true})
wrapDefinitionV2({...generateWorkflow(alertUuid, "", "", false,[], [], triggers), isValid: true})
);
} else {
setDefinition(wrapDefinitionV2({...parseWorkflow(loadedAlertFile!, providers), isValid:true}));
Expand Down
185 changes: 111 additions & 74 deletions keep-ui/app/workflows/builder/editors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -292,81 +292,118 @@ function WorkflowEditorV2({
const isTrigger = ["manual", "alert", 'interval'].includes(key) ;
renderDivider = isTrigger && key === selectedNode ? !renderDivider : false;
return (
<div key={key}>
{ renderDivider && <Divider />}
{((key === selectedNode)||(!isTrigger)) && <Text className="capitalize">{key}</Text>}
{key === "manual" ? (
selectedNode === 'manual' && <div key={key}>
<input
type="checkbox"
checked={true}
onChange={(e) =>
setProperties({
...properties,
[key]: e.target.checked ? "true" : "false",
})
}
disabled={true}
/>
<div key={key}>
{renderDivider && <Divider />}
{((key === selectedNode) || (!isTrigger)) && <Text className="capitalize">{key}</Text>}

{(() => {
switch (key) {
case "manual":
return (
selectedNode === "manual" && (
<div key={key}>
<input
type="checkbox"
checked={true}
onChange={(e) =>
setProperties({
...properties,
[key]: e.target.checked ? "true" : "false",
})
}
disabled={true}
/>
</div>
)
);

case "alert":
return (
selectedNode === "alert" && (
<>
<div className="w-1/2">
<Button
onClick={addFilter}
size="xs"
className="ml-1 mt-1"
variant="light"
color="gray"
icon={FunnelIcon}
>
Add Filter
</Button>
</div>
{properties.alert &&
Object.keys(properties.alert as {}).map((filter) => {
return (
<>
<Subtitle className="mt-2.5">{filter}</Subtitle>
<div className="flex items-center mt-1" key={filter}>
<TextInput
key={filter}
placeholder={`Set alert ${filter}`}
onChange={(e: any) =>
updateAlertFilter(filter, e.target.value)
}
value={(properties.alert as any)[filter] as string}
/>
<Icon
icon={BackspaceIcon}
className="cursor-pointer"
color="red"
tooltip={`Remove ${filter} filter`}
onClick={() => deleteFilter(filter)}
/>
</div>
</>
);
})}
</>
)
);

case "interval":
return (
selectedNode === "interval" && (
<TextInput
placeholder={`Set the ${key}`}
onChange={(e: any) =>
setProperties({ ...properties, [key]: e.target.value })
}
value={properties[key] as string}
/>
)
);
case "disabled":
return (
<div key={key}>
<input
type="checkbox"
checked={properties[key] === "true"}
onChange={(e) =>
setProperties({
...properties,
[key]: e.target.checked ? "true" : "false",
})
}
/>
</div>
);
default:
return (
<TextInput
placeholder={`Set the ${key}`}
onChange={(e: any) =>
setProperties({ ...properties, [key]: e.target.value })
}
value={properties[key] as string}
/>
);
}
})()}
</div>
) : key === "alert" ? (
selectedNode === 'alert' && <>
<div className="w-1/2">
<Button
onClick={addFilter}
size="xs"
className="ml-1 mt-1"
variant="light"
color="gray"
icon={FunnelIcon}
>
Add Filter
</Button>
</div>
{properties.alert &&
Object.keys(properties.alert as {}).map((filter) => {
return (
<>
<Subtitle className="mt-2.5">{filter}</Subtitle>
<div className="flex items-center mt-1" key={filter}>
<TextInput
key={filter}
placeholder={`Set alert ${filter}`}
onChange={(e: any) =>
updateAlertFilter(filter, e.target.value)
}
value={(properties.alert as any)[filter] as string}
/>
<Icon
icon={BackspaceIcon}
className="cursor-pointer"
color="red"
tooltip={`Remove ${filter} filter`}
onClick={() => deleteFilter(filter)}
/>
</div>
</>
);
})}
</>
) : key === "interval" ? (
selectedNode === 'interval' && <TextInput
placeholder={`Set the ${key}`}
onChange={(e: any) =>
setProperties({ ...properties, [key]: e.target.value })
}
value={properties[key] as string}
/>
): <TextInput
placeholder={`Set the ${key}`}
onChange={(e: any) =>
setProperties({ ...properties, [key]: e.target.value })
}
value={properties[key] as string}
/>}

</div>
);
);

})}
</>
);
Expand Down
5 changes: 5 additions & 0 deletions keep-ui/app/workflows/builder/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export function generateWorkflow(
workflowId: string,
name: string,
description: string,
disabled: boolean,
steps: V2Step[],
conditions: V2Step[],
triggers: { [key: string]: { [key: string]: string } } = {}
Expand All @@ -225,6 +226,7 @@ export function generateWorkflow(
id: workflowId,
name: name,
description: description,
disabled:disabled,
isLocked: true,
...triggers,
},
Expand Down Expand Up @@ -305,6 +307,7 @@ export function parseWorkflow(
workflow.id,
workflow.name,
workflow.description,
workflow.disabled,
steps,
conditions,
triggers
Expand Down Expand Up @@ -384,6 +387,7 @@ export function buildAlert(definition: Definition): Alert {
const alertId = alert.properties.id as string;
const name = (alert.properties.name as string) ?? "";
const description = (alert.properties.description as string) ?? "";
const disabled = (alert.properties.disabled) ?? false
const owners = (alert.properties.owners as string[]) ?? [];
const services = (alert.properties.services as string[]) ?? [];
// Steps (move to func?)
Expand Down Expand Up @@ -510,6 +514,7 @@ export function buildAlert(definition: Definition): Alert {
name: name,
triggers: triggers,
description: description,
disabled : Boolean(disabled),
owners: owners,
services: services,
steps: steps,
Expand Down
1 change: 1 addition & 0 deletions keep-ui/app/workflows/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type Workflow = {
interval: string;
providers: Provider[];
triggers: Trigger[];
disabled:boolean,
last_execution_time: string;
last_execution_status: string;
last_updated: string;
Expand Down
9 changes: 6 additions & 3 deletions keep-ui/app/workflows/workflow-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Fragment } from "react";
import { EllipsisHorizontalIcon } from "@heroicons/react/20/solid";
import { Icon } from "@tremor/react";
import { EyeIcon, PencilIcon, PlayIcon, TrashIcon, WrenchIcon } from "@heroicons/react/24/outline";
import { DownloadIcon } from "@radix-ui/react-icons";
import {DownloadIcon, LockClosedIcon, LockOpen1Icon} from "@radix-ui/react-icons";

interface WorkflowMenuProps {
onDelete?: () => Promise<void>;
Expand All @@ -14,6 +14,7 @@ interface WorkflowMenuProps {
allProvidersInstalled: boolean;
hasManualTrigger: boolean;
hasAlertTrigger: boolean;
isWorkflowDisabled:boolean
}


Expand All @@ -25,18 +26,20 @@ export default function WorkflowMenu({
onBuilder,
allProvidersInstalled,
hasManualTrigger,
hasAlertTrigger
hasAlertTrigger,
isWorkflowDisabled,
}: WorkflowMenuProps) {
const getDisabledTooltip = () => {
if (!allProvidersInstalled) return "Not all providers are installed.";
if (!hasManualTrigger) return "No manual trigger available.";
if (isWorkflowDisabled) return "Workflow is disabled";
return "";
};
const stopPropagation = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
};

const isRunButtonDisabled = !allProvidersInstalled || (!hasManualTrigger && !hasAlertTrigger);
const isRunButtonDisabled = !allProvidersInstalled || (!hasManualTrigger && !hasAlertTrigger) || isWorkflowDisabled;


return (
Expand Down
7 changes: 7 additions & 0 deletions keep-ui/app/workflows/workflow-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function WorkflowMenuSection({
onView={onView}
onBuilder={onBuilder}
allProvidersInstalled={allProvidersInstalled}
isWorkflowDisabled={workflow.disabled}
hasManualTrigger={hasManualTrigger}
hasAlertTrigger={hasAlertTrigger}
/>
Expand Down Expand Up @@ -1080,6 +1081,12 @@ export function WorkflowTileOld({ workflow }: { workflow: Workflow }) {
: "N/A"}
</span>
</ListItem>
<ListItem>
<span>Disabled</span>
<span className="text-right">
{workflow?.disabled?.toString()}
</span>
</ListItem>
</List>

<Accordion className="mt-2.5">
Expand Down
10 changes: 6 additions & 4 deletions keep/api/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def get_workflows_that_should_run():
workflows_with_interval = (
session.query(Workflow)
.filter(Workflow.is_deleted == False)
.filter(Workflow.is_disabled == False)
.filter(Workflow.interval != None)
.filter(Workflow.interval > 0)
.all()
Expand Down Expand Up @@ -276,6 +277,7 @@ def add_or_update_workflow(
created_by,
interval,
workflow_raw,
is_disabled,
updated_by=None,
) -> Workflow:
with Session(engine, expire_on_commit=False) as session:
Expand All @@ -299,6 +301,7 @@ def add_or_update_workflow(
existing_workflow.revision += 1 # Increment the revision
existing_workflow.last_updated = datetime.now() # Update last_updated
existing_workflow.is_deleted = False
existing_workflow.is_disabled= is_disabled

else:
# Create a new workflow
Expand All @@ -310,6 +313,7 @@ def add_or_update_workflow(
created_by=created_by,
updated_by=updated_by, # Set updated_by to the provided value
interval=interval,
is_disabled =is_disabled,
workflow_raw=workflow_raw,
)
session.add(workflow)
Expand Down Expand Up @@ -495,7 +499,6 @@ def get_raw_workflow(tenant_id: str, workflow_id: str) -> str:
return None
return workflow.workflow_raw


def update_provider_last_pull_time(tenant_id: str, provider_id: str):
extra = {"tenant_id": tenant_id, "provider_id": provider_id}
logger.info("Updating provider last pull time", extra=extra)
Expand Down Expand Up @@ -1333,7 +1336,7 @@ def save_workflow_results(tenant_id, workflow_execution_id, workflow_results):
session.commit()


def get_workflow_id_by_name(tenant_id, workflow_name):
def get_workflow_by_name(tenant_id, workflow_name):
with Session(engine) as session:
workflow = session.exec(
select(Workflow)
Expand All @@ -1342,8 +1345,7 @@ def get_workflow_id_by_name(tenant_id, workflow_name):
.where(Workflow.is_deleted == False)
).first()

if workflow:
return workflow.id
return workflow


def get_previous_execution_id(tenant_id, workflow_id, workflow_execution_id):
Expand Down
Loading

0 comments on commit b23778b

Please sign in to comment.