diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 30c7d48360..65ff99ce1e 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -532,6 +532,7 @@ async fn run_slack_message_test_job( ) -> JsonResult { let mut fake_result = Map::new(); fake_result.insert("error".to_string(), json!(req.test_msg)); + fake_result.insert("success_result".to_string(), json!(req.test_msg)); let mut extra_args = Map::new(); extra_args.insert("channel".to_string(), json!(req.channel)); diff --git a/frontend/src/lib/components/ScheduleEditor.svelte b/frontend/src/lib/components/ScheduleEditor.svelte index 17b176fd5a..7ea1ec3fce 100644 --- a/frontend/src/lib/components/ScheduleEditor.svelte +++ b/frontend/src/lib/components/ScheduleEditor.svelte @@ -7,22 +7,20 @@ import Required from '$lib/components/Required.svelte' import SchemaForm from '$lib/components/SchemaForm.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' + import SlackHandler from '$lib/components/SlackHandler.svelte' import Toggle from '$lib/components/Toggle.svelte' import Tooltip from '$lib/components/Tooltip.svelte' - import { FlowService, JobService, ResourceService, ScheduleService, Script, ScriptService, WorkspaceService, type Flow } from '$lib/gen' + import { FlowService, ScheduleService, Script, ScriptService, type Flow } from '$lib/gen' import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' - import { canWrite, emptySchema, emptyString, formatCron, sendUserToast, tryEvery } from '$lib/utils' - import { faList, faSave, faRotate, faRotateRight, faTimes } from '@fortawesome/free-solid-svg-icons' - import { check } from 'svelte-awesome/icons' + import { canWrite, emptySchema, emptyString, formatCron, sendUserToast } from '$lib/utils' + import { faList, faSave } from '@fortawesome/free-solid-svg-icons' import { createEventDispatcher } from 'svelte' - import Icon from 'svelte-awesome' import { inferArgs } from '$lib/infer' import type { Schema, SupportedLanguage } from '$lib/common' import Section from '$lib/components/Section.svelte' const slackErrorHandler = 'hub/2431/slack/schedule-error-handler-slack' const slackRecoveryHandler = 'hub/2430/slack/schedule-recovery-handler-slack' - const workspaceSlackConnectionResource = 'f/slack_bot/bot_token' let initialPath = '' let edit = true @@ -32,11 +30,11 @@ let itemKind: 'flow' | 'script' = 'script' let errorHandleritemKind: 'flow' | 'script' = 'script' let errorHandlerPath: string | undefined = undefined - let recoveryHandlerPath: string | undefined = undefined let errorHandlerSelected: 'custom' | 'slack' = 'slack' let errorHandlerSchema: Schema | undefined = undefined let errorHandlerExtraArgs: Record = {} - let recoveryHandlerSelected: 'custom' | 'slack' = 'custom' + let recoveryHandlerPath: string | undefined = undefined + let recoveryHandlerSelected: 'custom' | 'slack' = 'slack' let recoveryHandlerItemKind: 'flow' | 'script' = 'script' let recoveryHandlerSchema: Schema | undefined = undefined let recoveryHandlerExtraArgs: Record = {} @@ -46,8 +44,6 @@ let script_path = '' let initialScriptPath = '' - let slackConnectionToken: {value: string, label:string} | undefined - let slackConnectionTestJob: {uuid: string, is_success: boolean, in_progress: boolean} | undefined export function openEdit(ePath: string, isFlow: boolean) { is_flow = isFlow @@ -71,6 +67,7 @@ script_path = initialScriptPath errorHandleritemKind = 'script' errorHandlerPath = undefined + recoveryHandlerPath = undefined timezone = Intl.DateTimeFormat().resolvedOptions().timeZone drawer?.openDrawer() } @@ -130,8 +127,8 @@ failedTimes = s.on_failure_times ?? 1 failedExact = s.on_failure_exact ?? false errorHandlerExtraArgs = s.on_failure_extra_args ?? {} - if (errorHandlerPath == slackErrorHandler) { - errorHandlerSelected = 'slack' + if (errorHandlerPath !== slackErrorHandler) { + errorHandlerSelected = 'custom' } } else { errorHandlerPath = undefined @@ -143,8 +140,8 @@ recoveryHandlerPath = splitted.slice(1)?.join('/') recoveredTimes = s.on_recovery_times ?? 1 recoveryHandlerExtraArgs = s.on_recovery_extra_args ?? {} - if (recoveryHandlerPath == slackRecoveryHandler) { - recoveryHandlerSelected = 'slack' + if (recoveryHandlerPath !== slackRecoveryHandler) { + recoveryHandlerSelected = 'custom' } } else { recoveryHandlerPath = undefined @@ -157,54 +154,12 @@ } } - async function sendSlackMessage(channel: string): Promise { - let submitted_job = await WorkspaceService.runSlackMessageTestJob({ - workspace: $workspaceStore!, - requestBody: { - hub_script_path: slackErrorHandler, - channel: channel, - test_msg: `This is a notification to test the connection between Slack and Windmill workspace '${$workspaceStore!}'` - } - }) - slackConnectionTestJob = { - uuid: submitted_job.job_uuid!, - in_progress: true, - is_success: false - } - tryEvery({ - tryCode: async () => { - const testResult = await JobService.getCompletedJob({ - workspace: $workspaceStore!, - id: slackConnectionTestJob!.uuid - }) - slackConnectionTestJob!.in_progress = false - slackConnectionTestJob!.is_success = testResult.success - }, - timeoutCode: async () => { - try { - await JobService.cancelQueuedJob({ - workspace: $workspaceStore!, - id: slackConnectionTestJob!.uuid, - requestBody: { - reason: 'Slack message not sent after after 5s' - } - }) - } catch (err) { - console.error(err) - } - }, - interval: 500, - timeout: 5000 - }) - - } - async function scheduleScript(): Promise { if (errorHandlerSelected === 'slack' && !emptyString(errorHandlerPath)) { - // If the error handler is Slack, we inject the slack token in the args here as it is expected by the script - if (slackConnectionToken !== undefined) { - errorHandlerExtraArgs["slack"] = slackConnectionToken.value - } + errorHandlerExtraArgs['slack'] = '$res:f/slack_bot/bot_token' + } + if (recoveryHandlerSelected === 'slack' && !emptyString(recoveryHandlerPath)) { + recoveryHandlerExtraArgs['slack'] = '$res:f/slack_bot/bot_token' } if (edit) { await ScheduleService.updateSchedule({ @@ -284,24 +239,6 @@ } } - async function loadSlackResources() { - const nc = ( - await ResourceService.listResource({ - workspace: $workspaceStore!, - resourceType: 'slack', - }) - ) - // filter out custom user token, use only the one created by the workspace Slack connection - .filter((x) => x.path == workspaceSlackConnectionResource) - .map((x) => ({ - value: "$res:" + x.path, - label: x.path - })) - if (nc.length == 1) { - slackConnectionToken = nc[0] - } - } - $: { if ($workspaceStore) { if (edit && path != '') { @@ -310,12 +247,6 @@ } } - $: { - if ($workspaceStore) { - loadSlackResources() - } - } - $: errorHandlerPath && loadHandlerScriptArgs(errorHandlerPath, [ 'path', @@ -515,72 +446,17 @@
This error handler takes no extra arguments
{/if} {/if} - {#if errorHandlerSchema && errorHandlerSchema.properties && Object.keys(errorHandlerSchema.properties).length === 0} -
This error handler takes no extra arguments
- {/if} {:else if errorHandlerSelected === 'slack'} - - errorHandlerPath = e.detail ? slackErrorHandler : undefined} - /> - - {#if slackConnectionToken !== undefined} - - {/if} - {#if !emptyString(errorHandlerPath) } - {#if slackConnectionToken === undefined} - -
-

- The workspace needs to be connected to Slack to use this feature. You can configure it here. -

- -
- -
- {:else} - - {#if slackConnectionTestJob !== undefined} -

- {#if slackConnectionTestJob.in_progress} - - {:else if slackConnectionTestJob.is_success} - - {:else} - - {/if} - Message sent via Windmill job {slackConnectionTestJob.uuid} -

- {/if} - {/if} - {/if} {/if} - {#if errorHandlerSelected === 'custom' || (errorHandlerSelected === 'slack' && errorHandlerPath !== undefined && slackConnectionToken !== undefined)} + {#if errorHandlerSelected === 'custom' || (errorHandlerSelected === 'slack' && errorHandlerPath !== undefined)}

- Custom Slack + Custom {#if recoveryHandlerSelected === 'custom'} @@ -654,40 +530,46 @@ target="_blank">Create from template {/if} + + {#if recoveryHandlerPath} +

Extra arguments

+ + {#if recoveryHandlerSchema && recoveryHandlerSchema.properties && Object.keys(recoveryHandlerSchema.properties).length === 0} +
This recovery handler takes no extra arguments
+ {/if} + {/if}
{:else if recoveryHandlerSelected === 'slack'} - You will receive a notification on the selected slack channel. - + {/if} -
-
-

Triggered when schedule recovered

- -

time{recoveredTimes > 1 ? 's in a row' : ''}

+ {#if recoveryHandlerSelected === 'custom' || (recoveryHandlerSelected === 'slack' && recoveryHandlerPath !== undefined)} +
+
+

Triggered when schedule recovered

+ +

time{recoveredTimes > 1 ? 's in a row' : ''}

+
-
- - {#if recoveryHandlerPath} -

{recoveryHandlerSelected === 'custom' ? 'Extra arguments' : 'Configuration'}

- - {#if recoveryHandlerSchema && recoveryHandlerSchema.properties && Object.keys(recoveryHandlerSchema.properties).length === 0} -
This recovery handler takes no extra arguments
- {/if} {/if}
diff --git a/frontend/src/lib/components/SlackHandler.svelte b/frontend/src/lib/components/SlackHandler.svelte new file mode 100644 index 0000000000..31b28f406d --- /dev/null +++ b/frontend/src/lib/components/SlackHandler.svelte @@ -0,0 +1,148 @@ + + + + handlerPath = e.detail ? handlerPathToSet : undefined} + /> + +{#if slackConnectionToken !== undefined} + +{/if} +{#if !emptyString(handlerPath) } + {#if slackConnectionToken === undefined} + +
+

+ The workspace needs to be connected to Slack to use this feature. You can configure it here. +

+ +
+
+ {:else} + + {#if slackConnectionTestJob !== undefined} +

+ {#if slackConnectionTestJob.in_progress} + + {:else if slackConnectionTestJob.is_success} + + {:else} + + {/if} + Message sent via Windmill job {slackConnectionTestJob.uuid} +

+ {/if} + {/if} +{/if}