mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-04 16:03:06 +00:00
workspace error handler
This commit is contained in:
@@ -58,7 +58,10 @@ use sqlx::{FromRow, Postgres, Transaction};
|
||||
use windmill_common::oauth2::InstanceEvent;
|
||||
use windmill_common::utils::not_found_if_none;
|
||||
|
||||
use crate::teams_ee::{edit_teams_command, workspaces_list_available_teams_ids, connect_teams};
|
||||
use crate::teams_ee::{
|
||||
connect_teams, edit_teams_command, run_teams_message_test_job,
|
||||
workspaces_list_available_teams_channels, workspaces_list_available_teams_ids,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref WORKSPACE_KEY_REGEXP: Regex = Regex::new("^[a-zA-Z0-9]{64}$").unwrap();
|
||||
@@ -76,12 +79,23 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/get_deploy_to", get(get_deploy_to))
|
||||
.route("/edit_slack_command", post(edit_slack_command))
|
||||
.route("/edit_teams_command", post(edit_teams_command))
|
||||
.route("/available_teams_ids", get(workspaces_list_available_teams_ids))
|
||||
.route(
|
||||
"/available_teams_ids",
|
||||
get(workspaces_list_available_teams_ids),
|
||||
)
|
||||
.route(
|
||||
"/available_teams_channels",
|
||||
get(workspaces_list_available_teams_channels),
|
||||
)
|
||||
.route("/connect_teams", post(connect_teams))
|
||||
.route(
|
||||
"/run_slack_message_test_job",
|
||||
post(run_slack_message_test_job),
|
||||
)
|
||||
.route(
|
||||
"/run_teams_message_test_job",
|
||||
post(run_teams_message_test_job),
|
||||
)
|
||||
.route("/edit_webhook", post(edit_webhook))
|
||||
.route("/edit_auto_invite", post(edit_auto_invite))
|
||||
.route("/edit_deploy_to", post(edit_deploy_to))
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
Error parsing /home/alex/windmill/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (3222:3)
|
||||
|
||||
3219 | schema:
|
||||
3220 | type: string
|
||||
3221 |
|
||||
3222 | /w/{workspace}/oauth/disconnec ...
|
||||
----------^
|
||||
3223 | post:
|
||||
3224 | summary: disconnect slack
|
||||
ParserError: Error parsing /home/alex/windmill/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (3222:3)
|
||||
|
||||
3219 | schema:
|
||||
3220 | type: string
|
||||
3221 |
|
||||
3222 | /w/{workspace}/oauth/disconnec ...
|
||||
----------^
|
||||
3223 | post:
|
||||
3224 | summary: disconnect slack
|
||||
at Object.parse (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23)
|
||||
at getResult (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22)
|
||||
at runNextPlugin (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32)
|
||||
at /home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9
|
||||
at new Promise (<anonymous>)
|
||||
at Object.run (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12)
|
||||
at parseFile (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38)
|
||||
at parse (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30)
|
||||
at async $RefParser.parse (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28)
|
||||
at async $RefParser.resolve (/home/alex/windmill/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13)
|
||||
@@ -5,6 +5,7 @@
|
||||
import type { Schema, SupportedLanguage } from '$lib/common'
|
||||
import { base } from '$lib/base'
|
||||
import { enterpriseLicense, workspaceStore } from '$lib/stores'
|
||||
import MsTeamsIcon from '$lib/components/icons/MSTeamsIcon.svelte'
|
||||
import { emptySchema, emptyString, sendUserToast, tryEvery } from '$lib/utils'
|
||||
import {
|
||||
FlowService,
|
||||
@@ -14,21 +15,26 @@
|
||||
WorkspaceService,
|
||||
type Flow
|
||||
} from '$lib/gen'
|
||||
import type { ListAvailableTeamsChannelsResponse } from '$lib/gen/types.gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { hubBaseUrlStore } from '$lib/stores'
|
||||
|
||||
import { CheckCircle2, Loader2, RotateCw, XCircle } from 'lucide-svelte'
|
||||
import { CheckCircle2, Loader2, RotateCw, XCircle, RefreshCcw } from 'lucide-svelte'
|
||||
import { hubPaths } from '$lib/hub'
|
||||
|
||||
const slackRecoveryHandler = hubPaths.slackRecoveryHandler
|
||||
const slackHandlerScriptPath = hubPaths.slackErrorHandler
|
||||
const slackSuccessHandler = hubPaths.slackSuccessHandler
|
||||
|
||||
const teamsRecoveryHandler = hubPaths.teamsRecoveryHandler
|
||||
const teamsHandlerScriptPath = hubPaths.teamsErrorHandler
|
||||
const teamsSuccessHandler = hubPaths.teamsSuccessHandler
|
||||
|
||||
export let errorOrRecovery: 'error' | 'recovery' | 'success'
|
||||
export let isEditable: boolean
|
||||
export let slackToggleText: string = 'Enable'
|
||||
export let toggleText: string = 'Enable'
|
||||
export let showScriptHelpText: boolean = false
|
||||
export let handlerSelected: 'custom' | 'slack'
|
||||
export let handlerSelected: 'custom' | 'slack' | 'teams'
|
||||
|
||||
export let handlerPath: string | undefined
|
||||
export let handlerExtraArgs: Record<string, any>
|
||||
@@ -36,13 +42,18 @@
|
||||
export let customInitialScriptPath: string | undefined
|
||||
export let customScriptTemplate: string
|
||||
export let customHandlerKind: 'flow' | 'script' = 'script'
|
||||
let customHandlerSchema: Schema | undefined
|
||||
|
||||
let customHandlerSchema: Schema | undefined
|
||||
let slackHandlerSchema: Schema | undefined
|
||||
let teamsHandlerSchema: Schema | undefined
|
||||
let isFetching: boolean = false
|
||||
|
||||
let teams_channels: ListAvailableTeamsChannelsResponse = []
|
||||
|
||||
let workspaceConnectedToSlack: boolean | undefined = undefined
|
||||
let slackConnectionTestJob:
|
||||
| { uuid: string; is_success: boolean; in_progress: boolean }
|
||||
| undefined
|
||||
let workspaceConnectedToTeams: boolean | undefined = undefined
|
||||
|
||||
let connectionTestJob: { uuid: string; is_success: boolean; in_progress: boolean } | undefined
|
||||
|
||||
async function loadSlackResources() {
|
||||
const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
@@ -53,16 +64,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function sendSlackMessage(channel: string): Promise<void> {
|
||||
let submitted_job = await WorkspaceService.runSlackMessageTestJob({
|
||||
async function loadTeamsResources() {
|
||||
isFetching = true
|
||||
const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
if (!emptyString(settings.teams_team_name) && !emptyString(settings.teams_team_id)) {
|
||||
workspaceConnectedToTeams = true
|
||||
} else {
|
||||
workspaceConnectedToTeams = false
|
||||
}
|
||||
|
||||
teams_channels = await WorkspaceService.listAvailableTeamsChannels({ workspace: $workspaceStore! })
|
||||
isFetching = false
|
||||
}
|
||||
|
||||
async function sendMessage(channel: string, platform: 'teams' | 'slack'): Promise<void> {
|
||||
const testJobFunction =
|
||||
platform === 'slack'
|
||||
? WorkspaceService.runSlackMessageTestJob
|
||||
: WorkspaceService.runTeamsMessageTestJob
|
||||
|
||||
let submitted_job = await testJobFunction({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
hub_script_path: handlerPath,
|
||||
channel: channel,
|
||||
test_msg: `This is a notification to test the connection between Slack and Windmill workspace '${$workspaceStore!}'`
|
||||
test_msg: `This is a notification to test the connection between ${platform} and Windmill workspace '${$workspaceStore!}'`
|
||||
}
|
||||
})
|
||||
slackConnectionTestJob = {
|
||||
|
||||
connectionTestJob = {
|
||||
uuid: submitted_job.job_uuid!,
|
||||
in_progress: true,
|
||||
is_success: false
|
||||
@@ -71,16 +101,16 @@
|
||||
tryCode: async () => {
|
||||
const testResult = await JobService.getCompletedJob({
|
||||
workspace: $workspaceStore!,
|
||||
id: slackConnectionTestJob!.uuid
|
||||
id: connectionTestJob!.uuid
|
||||
})
|
||||
slackConnectionTestJob!.in_progress = false
|
||||
slackConnectionTestJob!.is_success = testResult.success
|
||||
connectionTestJob!.in_progress = false
|
||||
connectionTestJob!.is_success = testResult.success
|
||||
},
|
||||
timeoutCode: async () => {
|
||||
try {
|
||||
await JobService.cancelQueuedJob({
|
||||
workspace: $workspaceStore!,
|
||||
id: slackConnectionTestJob!.uuid,
|
||||
id: connectionTestJob!.uuid,
|
||||
requestBody: {
|
||||
reason: 'Slack message not sent after 5s'
|
||||
}
|
||||
@@ -94,6 +124,14 @@
|
||||
})
|
||||
}
|
||||
|
||||
async function sendSlackMessage(channel: string): Promise<void> {
|
||||
await sendMessage(channel, 'slack')
|
||||
}
|
||||
|
||||
async function sendTeamsMessage(channel: string): Promise<void> {
|
||||
await sendMessage(channel, 'teams')
|
||||
}
|
||||
|
||||
async function loadHandlerScriptArgs(p: string, defaultArgs: string[] = []) {
|
||||
try {
|
||||
let schema: Schema | undefined = emptySchema()
|
||||
@@ -145,14 +183,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
function isTeamsHandler(scriptPath: string | undefined) {
|
||||
if (scriptPath === undefined) {
|
||||
return false
|
||||
}
|
||||
if (errorOrRecovery == 'error') {
|
||||
return (
|
||||
scriptPath.startsWith('hub/') &&
|
||||
scriptPath.endsWith('/workspace-or-schedule-error-handler-teams')
|
||||
)
|
||||
} else if (errorOrRecovery == 'recovery') {
|
||||
return (
|
||||
scriptPath.startsWith('hub/') && scriptPath.endsWith('/schedule-recovery-handler-teams')
|
||||
)
|
||||
} else {
|
||||
return scriptPath.startsWith('hub/') && scriptPath.endsWith('/schedule-success-handler-teams')
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if ($workspaceStore) {
|
||||
loadSlackResources()
|
||||
loadTeamsResources()
|
||||
}
|
||||
}
|
||||
|
||||
$: handlerPath &&
|
||||
!isSlackHandler(handlerPath) &&
|
||||
!isTeamsHandler(handlerPath) &&
|
||||
loadHandlerScriptArgs(handlerPath, [
|
||||
'path',
|
||||
'workspace_id',
|
||||
@@ -187,11 +245,33 @@
|
||||
'email',
|
||||
'slack'
|
||||
]).then((schema) => (slackHandlerSchema = schema))
|
||||
|
||||
$: handlerPath &&
|
||||
isTeamsHandler(handlerPath) &&
|
||||
loadHandlerScriptArgs(handlerPath, [
|
||||
'path',
|
||||
'workspace_id',
|
||||
'job_id',
|
||||
'is_flow',
|
||||
'schedule_path',
|
||||
'error',
|
||||
'error_started_at',
|
||||
'failed_times',
|
||||
'started_at',
|
||||
'success_times',
|
||||
'success_result',
|
||||
'success_started_at',
|
||||
'email',
|
||||
'teams'
|
||||
]).then((schema) => (teamsHandlerSchema = schema))
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Tabs bind:selected={handlerSelected} class="mt-2 mb-4">
|
||||
<Tabs bind:selected={handlerSelected} on:change={() => {
|
||||
//TODO
|
||||
}} class="mt-2 mb-4">
|
||||
<Tab value="slack" disabled={!isEditable}>Slack</Tab>
|
||||
<Tab value="teams" disabled={!isEditable}>Teams</Tab>
|
||||
<Tab value="custom" disabled={!isEditable}>
|
||||
Custom
|
||||
<slot name="custom-tab-tooltip" />
|
||||
@@ -254,7 +334,7 @@
|
||||
<Toggle
|
||||
disabled={!$enterpriseLicense || !isEditable}
|
||||
checked={isSlackHandler(handlerPath)}
|
||||
options={{ right: slackToggleText }}
|
||||
options={{ right: toggleText }}
|
||||
on:change={async (e) => {
|
||||
if (e.detail && errorOrRecovery === 'error') {
|
||||
handlerPath = slackHandlerScriptPath
|
||||
@@ -313,11 +393,11 @@
|
||||
on:click={() => sendSlackMessage(handlerExtraArgs['channel'])}
|
||||
size="xs">Send test message</Button
|
||||
>
|
||||
{#if slackConnectionTestJob !== undefined}
|
||||
{#if connectionTestJob !== undefined}
|
||||
<p class="text-normal text-2xs mt-1 gap-2">
|
||||
{#if slackConnectionTestJob.in_progress}
|
||||
{#if connectionTestJob.in_progress}
|
||||
<RotateCw size={14} />
|
||||
{:else if slackConnectionTestJob.is_success}
|
||||
{:else if connectionTestJob.is_success}
|
||||
<CheckCircle2 size={14} class="text-green-600" />
|
||||
{:else}
|
||||
<XCircle size={14} class="text-red-700" />
|
||||
@@ -325,9 +405,103 @@
|
||||
Message sent via Windmill job
|
||||
<a
|
||||
target="_blank"
|
||||
href={`${base}/run/${slackConnectionTestJob.uuid}?workspace=${$workspaceStore}`}
|
||||
href={`${base}/run/${connectionTestJob.uuid}?workspace=${$workspaceStore}`}
|
||||
>
|
||||
{slackConnectionTestJob.uuid}
|
||||
{connectionTestJob.uuid}
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
{:else if handlerSelected === 'teams'}
|
||||
<span class="w-full flex mb-3">
|
||||
<Toggle
|
||||
disabled={!$enterpriseLicense || !isEditable}
|
||||
checked={isTeamsHandler(handlerPath)}
|
||||
options={{ right: toggleText }}
|
||||
on:change={async (e) => {
|
||||
if (e.detail && errorOrRecovery === 'error') {
|
||||
handlerPath = teamsHandlerScriptPath
|
||||
} else if (e.detail && errorOrRecovery === 'recovery') {
|
||||
handlerPath = teamsRecoveryHandler
|
||||
} else if (e.detail && errorOrRecovery === 'success') {
|
||||
handlerPath = teamsSuccessHandler
|
||||
} else {
|
||||
handlerPath = undefined
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
{#if workspaceConnectedToTeams}
|
||||
<div class="w-1/2 flex flex-row items-center gap-2">
|
||||
<div class="pt-1 flex-shrink-0">
|
||||
<MsTeamsIcon height="24px" width="24px" />
|
||||
</div>
|
||||
<p class="text-sm">Teams Channel</p>
|
||||
<div class="flex-grow">
|
||||
<select class="w-full" bind:value={handlerExtraArgs['teams_channel']}>
|
||||
{#if teams_channels.length === 0}
|
||||
<option value="" disabled selected>Bot not connected to any channel</option>
|
||||
{:else}
|
||||
<option value="" disabled selected>Select Teams channel</option>
|
||||
{#each teams_channels as channel}
|
||||
<option value={channel.channel_id}>
|
||||
{channel.channel_name}
|
||||
</option>
|
||||
{/each}
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<button on:click={loadTeamsResources} class="flex items-center gap-1 mt-2">
|
||||
<RefreshCcw size={16} class={isFetching ? 'animate-spin' : ''} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if workspaceConnectedToTeams == undefined}
|
||||
<Loader2 class="animate-spin" size={10} />
|
||||
{/if}
|
||||
{#if $enterpriseLicense && isTeamsHandler(handlerPath)}
|
||||
{#if workspaceConnectedToTeams == false}
|
||||
<Alert type="error" title="Workspace not connected to Teams">
|
||||
<div class="flex flex-row gap-x-1 w-full items-center">
|
||||
<p class="text-clip grow min-w-0">
|
||||
The workspace needs to be connected to Teams to use this feature. You can <a
|
||||
target="_blank"
|
||||
href="{base}/workspace_settings?tab=slack">configure it here</a
|
||||
>.
|
||||
</p>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
on:click={loadTeamsResources}
|
||||
startIcon={{ icon: RotateCw }}
|
||||
/>
|
||||
</div>
|
||||
</Alert>
|
||||
{:else}
|
||||
<Button
|
||||
disabled={emptyString(handlerExtraArgs['teams_channel'])}
|
||||
btnClasses="w-32 text-center"
|
||||
color="dark"
|
||||
on:click={() => sendTeamsMessage(handlerExtraArgs['teams_channel'])}
|
||||
size="xs">Send test message</Button
|
||||
>
|
||||
{#if connectionTestJob !== undefined}
|
||||
<p class="text-normal text-2xs mt-1 gap-2">
|
||||
{#if connectionTestJob.in_progress}
|
||||
<RotateCw size={14} />
|
||||
{:else if connectionTestJob.is_success}
|
||||
<CheckCircle2 size={14} class="text-green-600" />
|
||||
{:else}
|
||||
<XCircle size={14} class="text-red-700" />
|
||||
{/if}
|
||||
Message sent via Windmill job
|
||||
<a
|
||||
target="_blank"
|
||||
href={`${base}/run/${connectionTestJob.uuid}?workspace=${$workspaceStore}`}
|
||||
>
|
||||
{connectionTestJob.uuid}
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
@@ -842,7 +842,7 @@
|
||||
bind:handlerSelected={errorHandlerSelected}
|
||||
bind:handlerPath={errorHandlerPath}
|
||||
customInitialScriptPath={errorHandlerCustomInitialPath}
|
||||
slackToggleText="Alert channel on error"
|
||||
toggleText="Alert channel on error"
|
||||
customScriptTemplate="/scripts/add?hub=hub%2F9081%2Fwindmill%2Fschedule_error_handler_template"
|
||||
bind:customHandlerKind={errorHandleritemKind}
|
||||
bind:handlerExtraArgs={errorHandlerExtraArgs}
|
||||
@@ -937,7 +937,7 @@
|
||||
bind:handlerSelected={recoveryHandlerSelected}
|
||||
bind:handlerPath={recoveryHandlerPath}
|
||||
customInitialScriptPath={recoveryHandlerCustomInitialPath}
|
||||
slackToggleText="Alert channel when error recovered"
|
||||
toggleText="Alert channel when error recovered"
|
||||
customScriptTemplate="/scripts/add?hub=hub%2F9082%2Fwindmill%2Fschedule_recovery_handler_template"
|
||||
bind:customHandlerKind={recoveryHandlerItemKind}
|
||||
bind:handlerExtraArgs={recoveryHandlerExtraArgs}
|
||||
@@ -1030,7 +1030,7 @@
|
||||
bind:handlerSelected={successHandlerSelected}
|
||||
bind:handlerPath={successHandlerPath}
|
||||
customInitialScriptPath={successHandlerCustomInitialPath}
|
||||
slackToggleText="Alert channel when successful"
|
||||
toggleText="Alert channel when successful"
|
||||
customScriptTemplate="/scripts/add?hub=hub%2F9071%2Fwindmill%2Fschedule_success_handler_template"
|
||||
bind:customHandlerKind={successHandlerItemKind}
|
||||
bind:handlerExtraArgs={successHandlerExtraArgs}
|
||||
|
||||
@@ -66,6 +66,9 @@ type HubPaths = {
|
||||
slackReport: string
|
||||
discordReport: string
|
||||
smtpReport: string
|
||||
teamsErrorHandler: string
|
||||
teamsRecoveryHandler: string
|
||||
teamsSuccessHandler: string
|
||||
}
|
||||
|
||||
export const hubPaths = JSON.parse(rawHubPaths) as HubPaths
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
"slackErrorHandler_0": "hub/9079/workspace-or-schedule-error-handler-slack",
|
||||
"slackRecoveryHandler": "hub/9080/slack/schedule-recovery-handler-slack",
|
||||
"slackSuccessHandler": "hub/9072/slack/schedule-success-handler-slack",
|
||||
"teamsErrorHandler": "hub/9081/workspace-or-schedule-error-handler-teams",
|
||||
"teamsRecoveryHandler": "hub/9082/teams/schedule-recovery-handler-teams",
|
||||
"teamsSuccessHandler": "hub/9073/teams/schedule-success-handler-teams",
|
||||
"slackReport": "hub/9084/slack",
|
||||
"discordReport": "hub/9085/discord",
|
||||
"smtpReport": "hub/9086/smtp"
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
let customer_id: string | undefined = undefined
|
||||
let webhook: string | undefined = undefined
|
||||
let workspaceToDeployTo: string | undefined = undefined
|
||||
let errorHandlerSelected: 'custom' | 'slack' = 'slack'
|
||||
let errorHandlerSelected: 'custom' | 'slack' | 'teams' = 'slack'
|
||||
let errorHandlerInitialScriptPath: string
|
||||
let errorHandlerScriptPath: string
|
||||
let errorHandlerItemKind: 'flow' | 'script' = 'script'
|
||||
@@ -437,12 +437,11 @@
|
||||
if (emptyString($enterpriseLicense)) {
|
||||
errorHandlerSelected = 'custom'
|
||||
} else {
|
||||
errorHandlerSelected =
|
||||
emptyString(errorHandlerScriptPath) ||
|
||||
(errorHandlerScriptPath.startsWith('hub/') &&
|
||||
errorHandlerScriptPath.endsWith('/workspace-or-schedule-error-handler-slack'))
|
||||
? 'slack'
|
||||
: 'custom'
|
||||
errorHandlerSelected =
|
||||
emptyString(errorHandlerScriptPath) ? 'custom' :
|
||||
(errorHandlerScriptPath.startsWith('hub/') && errorHandlerScriptPath.endsWith('/workspace-or-schedule-error-handler-slack')) ? 'slack' :
|
||||
(errorHandlerScriptPath.endsWith('/workspace-or-schedule-error-handler-teams')) ? 'teams' :
|
||||
'custom'
|
||||
}
|
||||
errorHandlerExtraArgs = settings.error_handler_extra_args ?? {}
|
||||
codeCompletionEnabled = settings.code_completion_enabled
|
||||
@@ -991,7 +990,7 @@
|
||||
<div class="flex flex-col mt-5 gap-5 items-start">
|
||||
<Toggle
|
||||
disabled={!$enterpriseLicense ||
|
||||
(errorHandlerSelected === 'slack' &&
|
||||
((errorHandlerSelected === 'slack' || errorHandlerSelected === 'teams') &&
|
||||
!emptyString(errorHandlerScriptPath) &&
|
||||
emptyString(errorHandlerExtraArgs['channel']))}
|
||||
bind:checked={errorHandlerMutedOnCancel}
|
||||
@@ -999,7 +998,7 @@
|
||||
/>
|
||||
<Button
|
||||
disabled={!$enterpriseLicense ||
|
||||
(errorHandlerSelected === 'slack' &&
|
||||
((errorHandlerSelected === 'slack' || errorHandlerSelected === 'teams') &&
|
||||
!emptyString(errorHandlerScriptPath) &&
|
||||
emptyString(errorHandlerExtraArgs['channel']))}
|
||||
size="sm"
|
||||
|
||||
Reference in New Issue
Block a user