mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-19 00:03:19 +00:00
feat(frontend): Add seconds input (#1445)
* feat(frontend): Add seconds input * add more instances * fix header toggles * add retry calculations
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
import Icon from 'svelte-awesome'
|
||||
import { fade } from 'svelte/transition'
|
||||
import JsonEditor from './apps/editor/settingsPanel/inputEditor/JsonEditor.svelte'
|
||||
import { Badge, Button } from './common'
|
||||
import { Badge, Button, SecondsInput } from './common'
|
||||
import FieldHeader from './FieldHeader.svelte'
|
||||
import type ItemPicker from './ItemPicker.svelte'
|
||||
import NumberTypeNarrowing from './NumberTypeNarrowing.svelte'
|
||||
@@ -236,12 +236,12 @@
|
||||
<span>{extra['max']}</span>
|
||||
<span class="mx-2"><Badge large color="blue">{value}</Badge></span>
|
||||
</div>
|
||||
{:else if extra['seconds'] !== undefined}
|
||||
<SecondsInput bind:seconds={value} on:focus />
|
||||
{:else}
|
||||
<input
|
||||
{autofocus}
|
||||
on:focus={(e) => {
|
||||
dispatch('focus')
|
||||
}}
|
||||
on:focus
|
||||
{disabled}
|
||||
type="number"
|
||||
class={valid
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
export let variableEditor: VariableEditor | undefined = undefined
|
||||
export let itemPicker: ItemPicker | undefined = undefined
|
||||
export let noDynamicToggle = false
|
||||
export let argExtra: Record<string, any> = {}
|
||||
|
||||
let monaco: SimpleEditor | undefined = undefined
|
||||
let monacoTemplate: TemplateEditor | undefined = undefined
|
||||
@@ -236,7 +237,7 @@
|
||||
|
||||
<div class="max-w-xs" />
|
||||
<div
|
||||
class="relative mb-6 {$propPickerConfig?.propName == argName
|
||||
class="relative {$propPickerConfig?.propName == argName
|
||||
? 'outline outline-offset-0 outline-2 outline-blue-500 rounded-md'
|
||||
: ''}"
|
||||
>
|
||||
@@ -280,6 +281,7 @@
|
||||
bind:itemsType={schema.properties[argName].items}
|
||||
properties={schema.properties[argName].properties}
|
||||
displayHeader={false}
|
||||
extra={argExtra}
|
||||
{variableEditor}
|
||||
{itemPicker}
|
||||
bind:pickForField
|
||||
|
||||
@@ -10,6 +10,7 @@ export { default as Kbd } from './kbd/Kbd.svelte'
|
||||
export { default as Menu } from './menu/Menu.svelte'
|
||||
export { default as MenuItem } from './menu/MenuItem.svelte'
|
||||
export { default as Popup } from './popup/Popup.svelte'
|
||||
export { default as SecondsInput } from './seconds/SecondsInput.svelte'
|
||||
export { default as Skeleton } from './skeleton/Skeleton.svelte'
|
||||
export { default as Tab } from './tabs/Tab.svelte'
|
||||
export { default as TabContent } from './tabs/TabContent.svelte'
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<script lang="ts">
|
||||
type TimeUnit = number | undefined
|
||||
|
||||
const ONE_DAY_IN_SECONDS = 86400 as const
|
||||
const ONE_HOUR_IN_SECONDS = 3600 as const
|
||||
const ONE_MINUTE_IN_SECONDS = 60 as const
|
||||
|
||||
export let seconds = 0
|
||||
export let hideDisplay = false
|
||||
export let disabled = false
|
||||
let day: TimeUnit = undefined
|
||||
let hour: TimeUnit = undefined
|
||||
let min: TimeUnit = undefined
|
||||
let sec: TimeUnit = undefined
|
||||
|
||||
$: convertSecondsToTime(seconds)
|
||||
|
||||
function convertSecondsToTime(seconds) {
|
||||
day = Math.floor(seconds / ONE_DAY_IN_SECONDS)
|
||||
seconds -= day * ONE_DAY_IN_SECONDS
|
||||
day = day || undefined
|
||||
|
||||
hour = Math.floor(seconds / ONE_HOUR_IN_SECONDS)
|
||||
seconds -= hour * ONE_HOUR_IN_SECONDS
|
||||
hour = hour || undefined
|
||||
|
||||
min = Math.floor(seconds / ONE_MINUTE_IN_SECONDS)
|
||||
seconds -= min * ONE_MINUTE_IN_SECONDS
|
||||
min = min || undefined
|
||||
|
||||
sec = seconds || undefined
|
||||
}
|
||||
|
||||
function convertUnitsToSeconds() {
|
||||
seconds =
|
||||
(day || 0) * ONE_DAY_IN_SECONDS +
|
||||
(hour || 0) * ONE_HOUR_IN_SECONDS +
|
||||
(min || 0) * ONE_MINUTE_IN_SECONDS +
|
||||
(sec || 0)
|
||||
if (seconds < 0) seconds = 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if !hideDisplay}
|
||||
<input
|
||||
value={disabled ? '' : seconds + ' second' + (seconds === 1 ? '' : 's')}
|
||||
{disabled}
|
||||
readonly
|
||||
type="text"
|
||||
class="max-w-[248px] bg-gray-50 mb-2"
|
||||
/>
|
||||
{/if}
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs font-medium">
|
||||
<div class="flex items-center gap-2">
|
||||
<label>
|
||||
Sec
|
||||
<input
|
||||
type="number"
|
||||
class="!w-14"
|
||||
{disabled}
|
||||
bind:value={sec}
|
||||
on:change={convertUnitsToSeconds}
|
||||
on:focus
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Min
|
||||
<input
|
||||
type="number"
|
||||
class="!w-14"
|
||||
{disabled}
|
||||
bind:value={min}
|
||||
on:change={convertUnitsToSeconds}
|
||||
on:focus
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label>
|
||||
Hour
|
||||
<input
|
||||
type="number"
|
||||
class="!w-14"
|
||||
{disabled}
|
||||
bind:value={hour}
|
||||
on:change={convertUnitsToSeconds}
|
||||
on:focus
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Day
|
||||
<input
|
||||
type="number"
|
||||
class="!w-14"
|
||||
{disabled}
|
||||
bind:value={day}
|
||||
on:change={convertUnitsToSeconds}
|
||||
on:focus
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,6 +44,7 @@
|
||||
let modulePreview: ModulePreview
|
||||
let websocketAlive = { pyright: false, black: false, deno: false, go: false }
|
||||
let selected = 'inputs'
|
||||
let advancedSelected = 'retries'
|
||||
let wrapper: HTMLDivElement
|
||||
let panes: HTMLElement
|
||||
let totalTopGap = 0
|
||||
@@ -104,6 +105,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function selectAdvanced(subtab: string) {
|
||||
selected = 'advanced'
|
||||
advancedSelected = subtab
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
totalTopGap = 0
|
||||
if (!(wrapper && panes)) return
|
||||
@@ -127,10 +133,10 @@
|
||||
<svelte:fragment slot="header">
|
||||
<FlowModuleHeader
|
||||
bind:module={flowModule}
|
||||
on:toggleSuspend={() => (selected = 'advanced-suspend')}
|
||||
on:toggleSleep={() => (selected = 'advanced-sleep')}
|
||||
on:toggleRetry={() => (selected = 'advanced-retries')}
|
||||
on:toggleStopAfterIf={() => (selected = 'advanced-early-stop')}
|
||||
on:toggleSuspend={() => selectAdvanced('suspend')}
|
||||
on:toggleSleep={() => selectAdvanced('sleep')}
|
||||
on:toggleRetry={() => selectAdvanced('retries')}
|
||||
on:toggleStopAfterIf={() => selectAdvanced('early-stop')}
|
||||
on:fork={async () => {
|
||||
const [module, state] = await fork(flowModule)
|
||||
flowModule = module
|
||||
@@ -217,7 +223,7 @@
|
||||
<Tabs bind:selected>
|
||||
<Tab value="inputs"><span class="font-semibold">Step Input</span></Tab>
|
||||
<Tab value="test"><span class="font-semibold text-md">Test this step</span></Tab>
|
||||
<Tab value="advanced-retries">Advanced</Tab>
|
||||
<Tab value="advanced">Advanced</Tab>
|
||||
</Tabs>
|
||||
<div class="h-[calc(100%-32px)]">
|
||||
{#if selected === 'inputs'}
|
||||
@@ -240,42 +246,44 @@
|
||||
mod={flowModule}
|
||||
schema={$flowStateStore[$selectedId]?.schema ?? {}}
|
||||
/>
|
||||
{:else if selected.startsWith('advanced')}
|
||||
<Tabs bind:selected>
|
||||
<Tab value="advanced-retries">Retries</Tab>
|
||||
{:else if selected === 'advanced'}
|
||||
<Tabs bind:selected={advancedSelected}>
|
||||
<Tab value="retries">Retries</Tab>
|
||||
{#if !$selectedId.includes('failure')}
|
||||
<Tab value="advanced-early-stop">Early Stop/Break</Tab>
|
||||
<Tab value="advanced-suspend">Suspend</Tab>
|
||||
<Tab value="advanced-sleep">Sleep</Tab>
|
||||
<Tab value="advanced-same_worker">Shared Directory</Tab>
|
||||
<Tab value="early-stop">Early Stop/Break</Tab>
|
||||
<Tab value="suspend">Suspend</Tab>
|
||||
<Tab value="sleep">Sleep</Tab>
|
||||
<Tab value="same_worker">Shared Directory</Tab>
|
||||
{/if}
|
||||
</Tabs>
|
||||
{#if selected === 'advanced-retries'}
|
||||
<FlowRetries bind:flowModule class="px-4 pb-4 h-full overflow-auto" />
|
||||
{:else if selected === 'advanced-early-stop'}
|
||||
<FlowModuleEarlyStop bind:flowModule class="px-4 pb-4 h-full overflow-auto" />
|
||||
{:else if selected === 'advanced-suspend'}
|
||||
<div class="px-4 pb-4 h-full overflow-auto">
|
||||
<FlowModuleSuspend bind:flowModule />
|
||||
</div>
|
||||
{:else if selected === 'advanced-sleep'}
|
||||
<div class="px-4 pb-4 h-full overflow-auto">
|
||||
<FlowModuleSleep previousModuleId={previousModule?.id} bind:flowModule />
|
||||
</div>
|
||||
{:else if selected === 'advanced-same_worker'}
|
||||
<div class="p-4 h-full overflow-auto">
|
||||
<Alert type="info" title="Share a directory between steps">
|
||||
If shared directory is set, will share a folder that will be mounted on
|
||||
`./shared` for each of them to pass data between each other.
|
||||
</Alert>
|
||||
<Button
|
||||
btnClasses="mt-4"
|
||||
on:click={() => {
|
||||
$selectedId = 'settings-same-worker'
|
||||
}}>Set shared directory in the flow settings</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="h-[calc(100%-32px)] overflow-auto p-4">
|
||||
{#if advancedSelected === 'retries'}
|
||||
<FlowRetries bind:flowModule />
|
||||
{:else if advancedSelected === 'early-stop'}
|
||||
<FlowModuleEarlyStop bind:flowModule />
|
||||
{:else if advancedSelected === 'suspend'}
|
||||
<div>
|
||||
<FlowModuleSuspend bind:flowModule />
|
||||
</div>
|
||||
{:else if advancedSelected === 'sleep'}
|
||||
<div>
|
||||
<FlowModuleSleep previousModuleId={previousModule?.id} bind:flowModule />
|
||||
</div>
|
||||
{:else if advancedSelected === 'same_worker'}
|
||||
<div>
|
||||
<Alert type="info" title="Share a directory between steps">
|
||||
If shared directory is set, will share a folder that will be mounted on
|
||||
`./shared` for each of them to pass data between each other.
|
||||
</Alert>
|
||||
<Button
|
||||
btnClasses="mt-4"
|
||||
on:click={() => {
|
||||
$selectedId = 'settings-same-worker'
|
||||
}}>Set shared directory in the flow settings</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Pane>
|
||||
|
||||
@@ -20,14 +20,15 @@
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-start space-y-2 {$$props.class}">
|
||||
<h2 class="mt-2"
|
||||
>Early stop/Break<Tooltip>
|
||||
<h2>
|
||||
Early stop/Break
|
||||
<Tooltip>
|
||||
If defined, at the end of the step, the predicate expression will be evaluated to decide if
|
||||
the flow should stop early. Skipped flows are just a label useful to not see them in the runs
|
||||
page. If stop early is run within a forloop, it will just break the for-loop and have it stop
|
||||
at that iteration instead of stopping the whole flow.</Tooltip
|
||||
></h2
|
||||
>
|
||||
at that iteration instead of stopping the whole flow.
|
||||
</Tooltip>
|
||||
</h2>
|
||||
<Toggle
|
||||
checked={isStopAfterIfEnabled}
|
||||
on:change={() => {
|
||||
@@ -45,7 +46,7 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="w-full border p-2 flex flex-col {flowModule.stop_after_if ? '' : 'bg-gray-50'}">
|
||||
<div class="w-full border p-2 flex flex-col {flowModule.stop_after_if ? '' : 'bg-gray-50'}">
|
||||
{#if flowModule.stop_after_if}
|
||||
<Toggle
|
||||
bind:checked={flowModule.stop_after_if.skip_if_stopped}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
import type SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import { SecondsInput } from '../../common'
|
||||
|
||||
export let flowModule: FlowModule
|
||||
export let previousModuleId: string | undefined
|
||||
@@ -17,8 +17,7 @@
|
||||
|
||||
let schema = emptySchema()
|
||||
schema.properties['sleep'] = {
|
||||
type: 'number',
|
||||
description: 'Sleep time in seconds'
|
||||
type: 'number'
|
||||
}
|
||||
|
||||
let editor: SimpleEditor | undefined = undefined
|
||||
@@ -28,13 +27,14 @@
|
||||
$: isSleepEnabled = Boolean(flowModule.sleep)
|
||||
</script>
|
||||
|
||||
<h2 class="mt-2"
|
||||
>Sleep<Tooltip>
|
||||
<h2>
|
||||
Sleep
|
||||
<Tooltip>
|
||||
If defined, at the end of the step, the flow will sleep for a number of seconds before
|
||||
scheduling the next job (if any, no effect if the step is the last one). Sleeping is passive and
|
||||
does not consume any resources.</Tooltip
|
||||
></h2
|
||||
>
|
||||
does not consume any resources.
|
||||
</Tooltip>
|
||||
</h2>
|
||||
<Toggle
|
||||
checked={isSleepEnabled}
|
||||
on:change={() => {
|
||||
@@ -52,8 +52,7 @@
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<span class="text-xs font-bold">Sleep for duration (seconds)</span>
|
||||
|
||||
<span class="text-xs font-bold">Sleep for duration</span>
|
||||
{#if flowModule.sleep && schema.properties['sleep']}
|
||||
<div class="border">
|
||||
<PropPickerWrapper
|
||||
@@ -71,10 +70,11 @@
|
||||
argName="sleep"
|
||||
{schema}
|
||||
{previousModuleId}
|
||||
argExtra={{ seconds: true }}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<input type="number" disabled />
|
||||
<SecondsInput disabled />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
import { SecondsInput } from '../../common'
|
||||
|
||||
export let flowModule: FlowModule
|
||||
|
||||
@@ -16,14 +17,15 @@
|
||||
$: isSuspendEnabled = Boolean(flowModule.suspend)
|
||||
</script>
|
||||
|
||||
<h2 class="mt-2"
|
||||
>Suspend<Tooltip>
|
||||
<h2>
|
||||
Suspend
|
||||
<Tooltip>
|
||||
If defined, at the end of the step, the flow will be suspended until it receives external
|
||||
requests to be resumed or canceled. This is most useful to implement approval steps but can be
|
||||
used flexibly for other purpose. To get the resume urls, use `wmill.getResumeUrls()` in
|
||||
Typescript, or `wmill.get_resume_urls()` in Python.</Tooltip
|
||||
></h2
|
||||
>
|
||||
Typescript, or `wmill.get_resume_urls()` in Python.
|
||||
</Tooltip>
|
||||
</h2>
|
||||
<Toggle
|
||||
checked={isSuspendEnabled}
|
||||
on:change={() => {
|
||||
@@ -49,11 +51,11 @@
|
||||
<input type="number" disabled />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs font-bold">Timeout (in seconds)</span>
|
||||
<span class="text-xs font-bold">Timeout</span>
|
||||
|
||||
{#if flowModule.suspend}
|
||||
<input bind:value={flowModule.suspend.timeout} type="number" min="1" placeholder="1800" />
|
||||
<SecondsInput bind:seconds={flowModule.suspend.timeout} />
|
||||
{:else}
|
||||
<input type="number" disabled />
|
||||
<SecondsInput disabled />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
flowModule.retry = {
|
||||
...flowModule.retry,
|
||||
constant: {
|
||||
attempts: 0,
|
||||
seconds: 0
|
||||
attempts: 1,
|
||||
seconds: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,9 @@
|
||||
flowModule.retry = {
|
||||
...flowModule.retry,
|
||||
exponential: {
|
||||
attempts: 0,
|
||||
attempts: 1,
|
||||
multiplier: 1,
|
||||
seconds: 0
|
||||
seconds: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,69 +30,121 @@
|
||||
$: isExponentialRetryEnabled = Boolean(flowModule.retry?.exponential)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-start space-y-1 {$$props.class}">
|
||||
<h2 class="mt-2"
|
||||
>Retries <Tooltip>
|
||||
<div class={$$props.class}>
|
||||
<h2>
|
||||
Retries
|
||||
<Tooltip>
|
||||
If defined, upon error this step will be retried with a delay and a maximum number of attempts
|
||||
as defined below. If both static and exponential delay is defined, the static delay attempts
|
||||
are tried before the exponential ones.</Tooltip
|
||||
></h2
|
||||
>
|
||||
are tried before the exponential ones.
|
||||
</Tooltip>
|
||||
</h2>
|
||||
|
||||
<Toggle
|
||||
checked={isConstantRetryEnabled}
|
||||
on:change={() => {
|
||||
if (isConstantRetryEnabled && flowModule.retry?.constant) {
|
||||
flowModule.retry.constant = undefined
|
||||
} else {
|
||||
setConstantRetries()
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Constant retry enabled'
|
||||
}}
|
||||
/>
|
||||
{#if flowModule.retry?.constant}
|
||||
<span class="text-xs font-bold">Attempts</span>
|
||||
<input bind:value={flowModule.retry.constant.attempts} type="number" />
|
||||
<span class="text-xs font-bold">Delay (seconds)</span>
|
||||
<input bind:value={flowModule.retry.constant.seconds} type="number" />
|
||||
{:else}
|
||||
<span class="text-xs font-bold">Attempts</span>
|
||||
<input type="number" disabled />
|
||||
<span class="text-xs font-bold">Delay (seconds)</span>
|
||||
<input type="number" disabled />
|
||||
{/if}
|
||||
|
||||
<Toggle
|
||||
checked={isExponentialRetryEnabled}
|
||||
on:change={() => {
|
||||
if (isExponentialRetryEnabled && flowModule.retry?.exponential) {
|
||||
flowModule.retry.exponential = undefined
|
||||
} else {
|
||||
setExpoentialRetries()
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Exponential backoff enabled'
|
||||
}}
|
||||
/>
|
||||
{#if flowModule.retry?.exponential}
|
||||
<span class="text-xs font-bold">Attempts</span>
|
||||
<input bind:value={flowModule.retry.exponential.attempts} type="number" />
|
||||
<span class="text-xs font-bold">Mulitplier</span>
|
||||
<span class="text-xs text-gray-500">additional delay = multiplier * base ^ (# attempts)</span>
|
||||
<input bind:value={flowModule.retry.exponential.multiplier} type="number" />
|
||||
<span class="text-xs font-bold">Base</span>
|
||||
<input bind:value={flowModule.retry.exponential.seconds} type="number" />
|
||||
{:else}
|
||||
<span class="text-xs font-bold">Attempts</span>
|
||||
<input type="number" disabled />
|
||||
<span class="text-xs font-bold">Mulitplier</span>
|
||||
<span class="text-xs text-gray-500">additional delay = multiplier * seconds ^ (# attempts)</span
|
||||
>
|
||||
<input type="number" disabled />
|
||||
<span class="text-xs font-bold">Base</span>
|
||||
<input type="number" disabled />
|
||||
{/if}
|
||||
<div class="flex">
|
||||
<div class="w-1/2 mr-2">
|
||||
<div class="pt-4">
|
||||
<Toggle
|
||||
checked={isConstantRetryEnabled}
|
||||
on:change={() => {
|
||||
if (isConstantRetryEnabled && flowModule.retry?.constant) {
|
||||
flowModule.retry.constant = undefined
|
||||
} else {
|
||||
setConstantRetries()
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Constant retry enabled'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if flowModule.retry?.constant}
|
||||
<div class="text-xs font-bold !mt-2">Attempts</div>
|
||||
<input bind:value={flowModule.retry.constant.attempts} type="number" />
|
||||
<div class="text-xs font-bold !mt-2">Delay (in seconds)</div>
|
||||
<input bind:value={flowModule.retry.constant.seconds} type="number" />
|
||||
{:else}
|
||||
<div class="text-xs font-bold !mt-2">Attempts</div>
|
||||
<input type="number" disabled />
|
||||
<div class="text-xs font-bold !mt-2">Delay (in seconds)</div>
|
||||
<input type="number" disabled />
|
||||
{/if}
|
||||
<div class="pt-6">
|
||||
<Toggle
|
||||
checked={isExponentialRetryEnabled}
|
||||
on:change={() => {
|
||||
if (isExponentialRetryEnabled && flowModule.retry?.exponential) {
|
||||
flowModule.retry.exponential = undefined
|
||||
} else {
|
||||
setExpoentialRetries()
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Exponential backoff enabled'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if flowModule.retry?.exponential}
|
||||
<div class="text-xs font-bold !mt-2">Attempts</div>
|
||||
<input bind:value={flowModule.retry.exponential.attempts} type="number" />
|
||||
<div class="text-xs font-bold !mt-2">Mulitplier</div>
|
||||
<span class="text-xs text-gray-500">delay = multiplier * base ^ (number of attempt)</span>
|
||||
<input bind:value={flowModule.retry.exponential.multiplier} type="number" />
|
||||
<div class="text-xs font-bold !mt-2">Base (in seconds)</div>
|
||||
<input bind:value={flowModule.retry.exponential.seconds} type="number" />
|
||||
{:else}
|
||||
<div class="text-xs font-bold !mt-2">Attempts</div>
|
||||
<input type="number" disabled />
|
||||
<div class="text-xs font-bold !mt-2">Mulitplier</div>
|
||||
<span class="text-xs text-gray-500">delay = multiplier * base ^ (number of attempt)</span>
|
||||
<input type="number" disabled />
|
||||
<div class="text-xs font-bold !mt-2">Base (in seconds)</div>
|
||||
<input type="number" disabled />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="w-1/2 ml-2">
|
||||
{#if true}
|
||||
{@const { attempts: cAttempts, seconds: cSeconds } = flowModule.retry?.constant || {}}
|
||||
{@const {
|
||||
attempts: eAttempts,
|
||||
seconds: eSeconds,
|
||||
multiplier
|
||||
} = flowModule.retry?.exponential || {}}
|
||||
{@const cArray = Array.from({ length: cAttempts || 0 }, () => cSeconds)}
|
||||
{@const eArray = Array.from(
|
||||
{ length: eAttempts || 0 },
|
||||
(_, i) => (multiplier || 0) * (eSeconds || 0) ** (i + cArray.length + 1)
|
||||
)}
|
||||
{@const array = [...cArray, ...eArray]}
|
||||
<div
|
||||
class="bg-gray-50 border border-gray-300 rounded max-h-[431px] overflow-auto px-4 py-2"
|
||||
>
|
||||
<div class="text-xs font-medium mb-2">Retry attempts</div>
|
||||
{#if array.length > 0}
|
||||
<table class="text-xs">
|
||||
<tr>
|
||||
<td class="font-semibold pr-1">1:</td>
|
||||
<td class="pb-1">After {array[0]} second{array[0] === 1 ? '' : 's'}</td>
|
||||
</tr>
|
||||
{#each array.slice(1) as delay, i}
|
||||
{@const index = i + 2}
|
||||
<tr>
|
||||
<td class="font-semibold pr-1 align-top">{index}:</td>
|
||||
<td class="pb-1 whitespace-nowrap">
|
||||
{delay} second{delay === 1 ? '' : 's'} after attempt #{index - 1}
|
||||
{#if i > cArray.length - 2}
|
||||
<span class="text-gray-400 pl-2">
|
||||
({multiplier} * {eSeconds}<sup>{index}</sup>)
|
||||
</span>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
{:else}
|
||||
<div class="text-xs">No retries</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user