mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 08:00:59 +00:00
feat(forks): UI: opt-in clone-triggers checkbox + confirm-on-fork-conflict
Adds the user-facing surface for the fork-trigger work: - CreateWorkspaceInner: new "Clone triggers and schedules" toggle in the fork-creation dialog (default off). Sends fork_triggers in the request. - forkConflict utility: detects the `fork-conflict:<kind>:<parent_id>` error string from the backend, shows a confirm() dialog explaining why the action is blocked, retries with `force: true` if accepted. - Wires withForkConflictRetry into every trigger setMode and the schedule setEnabled call, both in the per-kind editor components and the +page.svelte list views (HTTP, websocket, kafka, NATS, SQS, MQTT, GCP, Azure, Postgres, email, schedule). OpenAPI spec gains the `force` field on each setmode/setenabled body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13009,6 +13009,11 @@ paths:
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
schedule in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- enabled
|
||||
|
||||
@@ -13455,6 +13460,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -13622,6 +13632,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -13822,6 +13837,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -14063,6 +14083,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -14258,6 +14283,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -15046,6 +15076,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -15241,6 +15276,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -15495,6 +15535,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -16041,6 +16086,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
@@ -16267,6 +16317,11 @@ paths:
|
||||
properties:
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
force:
|
||||
type: boolean
|
||||
description: >
|
||||
Bypass the parent-state conflict warning when enabling a
|
||||
trigger in a fork whose parent has the same path enabled.
|
||||
required:
|
||||
- mode
|
||||
responses:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import Path from '$lib/components/Path.svelte'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import {
|
||||
@@ -265,11 +266,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await AzureTriggerService.setAzureTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
AzureTriggerService.setAzureTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'Azure trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} Azure trigger ${initialPath}`)
|
||||
onUpdate?.(initialPath)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
} from '$lib/gen'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -257,11 +258,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await EmailTriggerService.setEmailTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
EmailTriggerService.setEmailTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'email trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} email trigger ${initialPath}`)
|
||||
|
||||
onUpdate(initialPath)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import Path from '$lib/components/Path.svelte'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import {
|
||||
@@ -280,11 +281,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await GcpTriggerService.setGcpTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
GcpTriggerService.setGcpTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'GCP Pub/Sub trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} GCP Pub/Sub trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
generateRandomString,
|
||||
sendUserToast
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2, Pipette, Plus } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -408,11 +409,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await HttpTriggerService.setHttpTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
HttpTriggerService.setHttpTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'HTTP trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} HTTP trigger ${initialPath}`)
|
||||
|
||||
onUpdate(initialPath)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { KafkaTriggerService, type ErrorHandler, type Retry, type TriggerMode } from '$lib/gen'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2, RotateCcw } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -318,11 +319,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await KafkaTriggerService.setKafkaTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
KafkaTriggerService.setKafkaTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'Kafka trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} Kafka trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import ScriptPicker from '$lib/components/ScriptPicker.svelte'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -304,11 +305,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await MqttTriggerService.setMqttTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
MqttTriggerService.setMqttTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'MQTT trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} MQTT trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { NatsTriggerService, type ErrorHandler, type Retry, type TriggerMode } from '$lib/gen'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -284,11 +285,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await NatsTriggerService.setNatsTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
NatsTriggerService.setNatsTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'NATS trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} NATS trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
} from '$lib/gen'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, emptyString, emptyStringTrimmed, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -445,11 +446,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await PostgresTriggerService.setPostgresTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
PostgresTriggerService.setPostgresTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'postgres trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} postgres trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
import WorkerTagPicker from '$lib/components/WorkerTagPicker.svelte'
|
||||
import { runScheduleNow } from '../scheduled/utils'
|
||||
import { handleConfigChange } from '../utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import TextInput from '$lib/components/text_input/TextInput.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import PermissionedAsLine from '../PermissionedAsLine.svelte'
|
||||
@@ -629,11 +630,15 @@
|
||||
async function handleToggleEnabled(nEnabled: boolean) {
|
||||
enabled = nEnabled
|
||||
if (!trigger?.draftConfig) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { enabled: nEnabled }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
ScheduleService.setScheduleEnabled({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { enabled: nEnabled, force }
|
||||
}),
|
||||
'schedule'
|
||||
)
|
||||
sendUserToast(`${nEnabled ? 'enabled' : 'disabled'} schedule ${initialPath}`)
|
||||
onUpdate?.(initialPath)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import Path from '$lib/components/Path.svelte'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import {
|
||||
@@ -242,11 +243,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await SqsTriggerService.setSqsTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
SqsTriggerService.setSqsTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'SQS trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} SQS trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
} from '$lib/gen'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, emptySchema, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Loader2, X, Plus } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
@@ -372,11 +373,15 @@
|
||||
async function handleToggleMode(newMode: TriggerMode) {
|
||||
mode = newMode
|
||||
if (!trigger?.draftConfig) {
|
||||
await WebsocketTriggerService.setWebsocketTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
WebsocketTriggerService.setWebsocketTriggerMode({
|
||||
path: initialPath,
|
||||
workspace: $workspaceStore ?? '',
|
||||
requestBody: { mode: newMode, force }
|
||||
}),
|
||||
'websocket trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(newMode)} websocket trigger ${initialPath}`)
|
||||
|
||||
onUpdate?.(initialPath)
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
|
||||
let workspaceColor: string | undefined = $state(undefined)
|
||||
let colorEnabled = $state(false)
|
||||
let forkTriggers = $state(false)
|
||||
|
||||
function generateRandomColor() {
|
||||
const randomColor =
|
||||
@@ -208,7 +209,8 @@
|
||||
id: prefixed_id,
|
||||
name,
|
||||
color: colorEnabled && workspaceColor ? workspaceColor : undefined,
|
||||
forked_datatables: forkedDatatables
|
||||
forked_datatables: forkedDatatables,
|
||||
fork_triggers: forkTriggers
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
@@ -507,6 +509,15 @@
|
||||
forkCreationLoading = false
|
||||
}}
|
||||
/>
|
||||
<Label label="Clone triggers and schedules">
|
||||
<span class="text-xs text-secondary">
|
||||
Copy every trigger and schedule from the parent into this fork. They start
|
||||
<span class="font-semibold">disabled</span> — re-enable the ones you need to test. Enabling
|
||||
something the parent already runs will require an explicit confirmation since two listeners
|
||||
can compete for the same upstream events.
|
||||
</span>
|
||||
<Toggle bind:checked={forkTriggers} options={{ right: 'Clone' }} />
|
||||
</Label>
|
||||
{/if}
|
||||
{#if !automateUsernameCreation}
|
||||
<Label label="Your username in that workspace">
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* The backend rejects "enable" requests on triggers/schedules in a fork when
|
||||
* the parent workspace has the same path enabled. The error body is shaped as
|
||||
* `fork-conflict:<kind>:<parent_workspace_id>`
|
||||
* so the UI can show a tailored confirm-to-proceed dialog and re-issue the
|
||||
* call with `force: true` if the user agrees.
|
||||
*/
|
||||
export interface ForkConflict {
|
||||
kind: string
|
||||
parentWorkspaceId: string
|
||||
}
|
||||
|
||||
export function detectForkConflict(e: unknown): ForkConflict | null {
|
||||
const body = (e as any)?.body
|
||||
const raw =
|
||||
typeof body === 'string'
|
||||
? body
|
||||
: ((body as any)?.error?.message ?? (body as any)?.message ?? (e as any)?.message ?? '')
|
||||
const m = String(raw).match(/fork-conflict:([^:]+):(.+)/)
|
||||
if (!m) return null
|
||||
return { kind: m[1], parentWorkspaceId: m[2].trim() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches a fork-conflict error from `fn(false)`, asks the user to confirm,
|
||||
* and retries with `fn(true)` when accepted. Re-throws every other error.
|
||||
*
|
||||
* `kindLabel` is shown to the user — pass a friendly name like "kafka trigger"
|
||||
* or "schedule" so the dialog reads naturally.
|
||||
*/
|
||||
export async function withForkConflictRetry<T>(
|
||||
fn: (force: boolean) => Promise<T>,
|
||||
kindLabel: string
|
||||
): Promise<T> {
|
||||
try {
|
||||
return await fn(false)
|
||||
} catch (e) {
|
||||
const conflict = detectForkConflict(e)
|
||||
if (!conflict) throw e
|
||||
const proceed = window.confirm(
|
||||
`This ${kindLabel} is also enabled in the parent workspace ` +
|
||||
`(${conflict.parentWorkspaceId}). Both will run at the same time and may ` +
|
||||
`compete for the same upstream events or duplicate side effects.\n\n` +
|
||||
`Enable in this fork anyway?`
|
||||
)
|
||||
if (!proceed) {
|
||||
throw new Error(
|
||||
`Enable cancelled because of fork conflict with ${conflict.parentWorkspaceId}`
|
||||
)
|
||||
}
|
||||
return await fn(true)
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
storeLocalSetting,
|
||||
removeTriggerKindIfUnused
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -113,11 +114,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await AzureTriggerService.setAzureTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
AzureTriggerService.setAzureTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'Azure trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ${mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend'} Azure Event Grid trigger: ${err.body}`,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
removeTriggerKindIfUnused,
|
||||
sendUserToast
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -227,11 +228,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await EmailTriggerService.setEmailTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
EmailTriggerService.setEmailTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'email trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ` +
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
storeLocalSetting,
|
||||
removeTriggerKindIfUnused
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -113,11 +114,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await GcpTriggerService.setGcpTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
GcpTriggerService.setGcpTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'GCP Pub/Sub trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ${mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend'} GCP Pub/Sub trigger: ${err.body}`,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
storeLocalSetting,
|
||||
removeTriggerKindIfUnused
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -106,11 +107,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await KafkaTriggerService.setKafkaTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
KafkaTriggerService.setKafkaTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'Kafka trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ` +
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
storeLocalSetting,
|
||||
removeTriggerKindIfUnused
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -101,11 +102,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await MqttTriggerService.setMqttTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
MqttTriggerService.setMqttTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'MQTT trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(mode)} MQTT trigger ${path}`)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
storeLocalSetting,
|
||||
removeTriggerKindIfUnused
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -105,11 +106,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await NatsTriggerService.setNatsTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
NatsTriggerService.setNatsTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'NATS trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ` +
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
removeTriggerKindIfUnused,
|
||||
capitalize
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -112,11 +113,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await PostgresTriggerService.setPostgresTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
PostgresTriggerService.setPostgresTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'postgres trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(mode)} postgres trigger ${path}`)
|
||||
} catch (err) {
|
||||
sendUserToast(`Cannot change postgres trigger mode: ${err.body}`, true)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
removeTriggerKindIfUnused,
|
||||
sendUserToast
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -243,11 +244,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await HttpTriggerService.setHttpTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
HttpTriggerService.setHttpTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'HTTP trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ` +
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
WorkspaceService
|
||||
} from '$lib/gen'
|
||||
import { canWrite, displayDate, getLocalSetting, storeLocalSetting } from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
import { Badge, Button, Skeleton } from '$lib/components/common'
|
||||
@@ -142,11 +143,15 @@
|
||||
|
||||
async function setScheduleEnabled(path: string, enabled: boolean): Promise<void> {
|
||||
try {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
ScheduleService.setScheduleEnabled({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { enabled, force }
|
||||
}),
|
||||
'schedule'
|
||||
)
|
||||
loadSchedules()
|
||||
} catch (err) {
|
||||
sendUserToast(`Cannot ` + (enabled ? 'enable' : 'disable') + ` schedule: ${err.body}`, true)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
storeLocalSetting,
|
||||
removeTriggerKindIfUnused
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -99,11 +100,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await SqsTriggerService.setSqsTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
SqsTriggerService.setSqsTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'SQS trigger'
|
||||
)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
`Cannot ` +
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
removeTriggerKindIfUnused,
|
||||
capitalize
|
||||
} from '$lib/utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/stores'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
@@ -99,11 +100,15 @@
|
||||
|
||||
async function onToggleMode(path: string, mode: TriggerMode): Promise<void> {
|
||||
try {
|
||||
await WebsocketTriggerService.setWebsocketTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode }
|
||||
})
|
||||
await withForkConflictRetry(
|
||||
(force) =>
|
||||
WebsocketTriggerService.setWebsocketTriggerMode({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { mode, force }
|
||||
}),
|
||||
'websocket trigger'
|
||||
)
|
||||
sendUserToast(`${capitalize(mode)} websocket trigger ${path}`)
|
||||
} catch (err) {
|
||||
sendUserToast(
|
||||
|
||||
Reference in New Issue
Block a user