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:
hugocasa
2026-04-29 18:47:32 +02:00
parent 65b80ecbeb
commit e6e2afa3f4
25 changed files with 340 additions and 111 deletions
+55
View File
@@ -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">
+53
View File
@@ -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(