Add on behaklft of selector for script flow apps (#8117)

This commit is contained in:
wendrul
2026-02-26 16:30:11 +00:00
committed by GitHub
parent 3d480ad562
commit 82c31e2c30
4 changed files with 155 additions and 83 deletions
@@ -26,15 +26,25 @@
targetWorkspace: string
targetEmail: string | undefined
selected: OnBehalfOfChoice
onSelect: (choice: OnBehalfOfChoice, email?: string) => void
onSelect: (choice: OnBehalfOfChoice, email?: string, username?: string) => void
kind: string
canPreserve: boolean
/** The email of the custom-selected user (for display) */
customEmail?: string | undefined
/** When false, labels say "current" instead of "target" and modal text refers to "this workspace" */
isDeployment?: boolean
}
let { targetWorkspace, targetEmail, selected, onSelect, kind, canPreserve, customEmail }: Props =
$props()
let {
targetWorkspace,
targetEmail,
selected,
onSelect,
kind,
canPreserve,
customEmail,
isDeployment = true
}: Props = $props()
let label = $derived(
kind === 'trigger'
@@ -96,7 +106,7 @@
}
function selectUser(user: User) {
onSelect('custom', user.email)
onSelect('custom', user.email, user.username)
modalOpen = false
}
@@ -130,7 +140,7 @@
>
<Check class="w-3 h-3 {selected === 'target' ? 'opacity-100' : 'opacity-0'}" />
<span class="truncate max-w-40">{targetUsername}</span>
<span class="text-xs text-tertiary">(target)</span>
<span class="text-xs text-tertiary">{isDeployment ? '(target)' : '(current)'}</span>
</button>
{/if}
<!-- Me option -->
@@ -171,9 +181,9 @@
<div class="flex flex-col gap-4">
<div class="text-xs text-secondary">
{#if kind === 'trigger'}
Choose the user this trigger will be recorded as edited by in the target workspace.
Choose the user this trigger will be recorded as edited by {isDeployment ? 'in the target workspace' : 'in this workspace'}.
{:else}
Choose the user this {kind} will run on behalf of in the target workspace. The selected
Choose the user this {kind} will run on behalf of {isDeployment ? 'in the target workspace' : 'in this workspace'}. The selected
user's permissions will be used when executing.
{/if}
<a
@@ -103,6 +103,7 @@
import { inputSizeClasses } from './text_input/TextInput.svelte'
import type { ButtonType } from './common/button/model'
import DebounceLimit from './flows/DebounceLimit.svelte'
import OnBehalfOfSelector, { type OnBehalfOfChoice } from './OnBehalfOfSelector.svelte'
let {
script = $bindable(),
@@ -161,13 +162,10 @@
const WM_DEPLOYERS_GROUP = 'wm_deployers'
let isDeployer = $derived($userStore?.groups?.includes(WM_DEPLOYERS_GROUP) ?? false)
let canPreserve = $derived(!!$userStore?.is_admin || !!$userStore?.is_super_admin || isDeployer)
let originalOnBehalfOfEmail = $derived(savedScript?.on_behalf_of_email)
let showPreserveToggle = $derived(
isDeployer &&
script.on_behalf_of_email &&
originalOnBehalfOfEmail &&
originalOnBehalfOfEmail !== $userStore?.email
)
let onBehalfOfChoice: OnBehalfOfChoice = $state(undefined)
let customOnBehalfOfEmail: string = $state('')
let metadataOpen = $state(
!neverShowMeta &&
@@ -1468,9 +1466,9 @@
>
In this mode, the script is meant to be run on dedicated workers that run
the script at native speed. Can reach &gt;1500rps per dedicated worker. Only
available on enterprise edition and for Python3, Deno, Bun and Bunnative. For other
languages, the efficiency is already on par with dedicated workers since
they do not spawn a full runtime</Tooltip
available on enterprise edition and for Python3, Deno, Bun and Bunnative.
For other languages, the efficiency is already on par with dedicated workers
since they do not spawn a full runtime</Tooltip
>
{/snippet}
</Section>
@@ -1597,30 +1595,51 @@
</Tooltip>
{/snippet}
<div class="flex gap-2 shrink flex-col">
<Toggle
size="sm"
checked={Boolean(script.on_behalf_of_email)}
on:change={() => {
if (script.on_behalf_of_email) {
script.on_behalf_of_email = undefined
preserveOnBehalfOf = false
} else {
script.on_behalf_of_email = $userStore?.email
}
}}
options={{
right: 'Run on behalf of last editor'
}}
/>
{#if showPreserveToggle}
<span class="inline-flex gap-2">
<Toggle
size="sm"
bind:checked={preserveOnBehalfOf}
checked={Boolean(script.on_behalf_of_email)}
on:change={() => {
if (script.on_behalf_of_email) {
script.on_behalf_of_email = undefined
preserveOnBehalfOf = false
onBehalfOfChoice = undefined
} else {
script.on_behalf_of_email = $userStore?.email
}
}}
options={{
right: `Keep original author (${originalOnBehalfOfEmail})`
right: `Run on behalf of ${canPreserve ? 'a specified user' : 'last editor'}`
}}
/>
{/if}
{#if script.on_behalf_of_email && canPreserve}
&rarr; <OnBehalfOfSelector
targetWorkspace={$workspaceStore ?? ''}
targetEmail={originalOnBehalfOfEmail}
selected={onBehalfOfChoice}
onSelect={(choice, email) => {
onBehalfOfChoice = choice
if (choice === 'me') {
script.on_behalf_of_email = $userStore?.email
customOnBehalfOfEmail = ''
preserveOnBehalfOf = false
} else if (choice === 'target') {
script.on_behalf_of_email = originalOnBehalfOfEmail
customOnBehalfOfEmail = ''
preserveOnBehalfOf = true
} else if (choice === 'custom' && email) {
script.on_behalf_of_email = email
customOnBehalfOfEmail = email
preserveOnBehalfOf = true
}
}}
kind="script"
{canPreserve}
customEmail={customOnBehalfOfEmail}
isDeployment={false}
/>
{/if}
</span>
</div>
</Section>
{#if !isCloudHosted()}
@@ -15,6 +15,9 @@
import { isCloudHosted } from '$lib/cloud'
import EEOnly from '$lib/components/EEOnly.svelte'
import TextInput from '$lib/components/text_input/TextInput.svelte'
import OnBehalfOfSelector, {
type OnBehalfOfChoice
} from '$lib/components/OnBehalfOfSelector.svelte'
const WM_DEPLOYERS_GROUP = 'wm_deployers'
@@ -51,13 +54,12 @@
} = $props()
let isDeployer = $derived($userStore?.groups?.includes(WM_DEPLOYERS_GROUP) ?? false)
let savedOnBehalfOfEmail = $derived(savedApp?.policy?.on_behalf_of_email)
let showPreserveToggle = $derived(
isDeployer &&
policy?.on_behalf_of_email &&
savedOnBehalfOfEmail &&
savedOnBehalfOfEmail !== $userStore?.email
let canPreserve = $derived(
!!$userStore?.is_admin || !!$userStore?.is_super_admin || isDeployer
)
let savedOnBehalfOfEmail = $derived(savedApp?.policy?.on_behalf_of_email)
let onBehalfOfChoice: OnBehalfOfChoice = $state(undefined)
let customOnBehalfOfEmail: string = $state('')
let dirtyCustomPath = $state(false)
let path: Path | undefined = $state(undefined)
@@ -193,19 +195,40 @@
Hence, users will only be able to use the app as intended by the publisher without risk for
leaking resources not used in the app.
</Tooltip>
{#if canPreserve}
<div class="mt-4">
Because you are either an admin or part of the {WM_DEPLOYERS_GROUP} group, you can select another user to run this app on behalf of. Once deployed the app will be run on behalf of
<OnBehalfOfSelector
targetWorkspace={$workspaceStore ?? ''}
targetEmail={savedOnBehalfOfEmail}
selected={onBehalfOfChoice}
onSelect={(choice, email, username) => {
onBehalfOfChoice = choice
if (choice === 'me') {
policy.on_behalf_of_email = $userStore?.email
policy.on_behalf_of = `u/${$userStore?.username}`
customOnBehalfOfEmail = ''
preserveOnBehalfOf = false
} else if (choice === 'target') {
policy.on_behalf_of_email = savedOnBehalfOfEmail
customOnBehalfOfEmail = ''
preserveOnBehalfOf = true
} else if (choice === 'custom' && email) {
policy.on_behalf_of_email = email
policy.on_behalf_of = username ? `u/${username}` : undefined
customOnBehalfOfEmail = email
preserveOnBehalfOf = true
}
}}
kind="app"
{canPreserve}
customEmail={customOnBehalfOfEmail}
isDeployment={false}
/>
</div>
{/if}
</Alert>
{#if showPreserveToggle}
<div class="mt-4">
<Toggle
size="sm"
bind:checked={preserveOnBehalfOf}
options={{
right: `Keep original author (${savedOnBehalfOfEmail})`
}}
/>
</div>
{/if}
<div class="mt-10"></div>
@@ -8,7 +8,7 @@
import { Alert, Button, SecondsInput } from '$lib/components/common'
import { getContext } from 'svelte'
import type { FlowEditorContext } from '../types'
import { enterpriseLicense, userStore } from '$lib/stores'
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
import { isCloudHosted } from '$lib/cloud'
import Tooltip from '$lib/components/Tooltip.svelte'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
@@ -26,6 +26,9 @@
import { slide } from 'svelte/transition'
import DebounceLimit from '../DebounceLimit.svelte'
import EEOnly from '$lib/components/EEOnly.svelte'
import OnBehalfOfSelector, {
type OnBehalfOfChoice
} from '$lib/components/OnBehalfOfSelector.svelte'
interface Props {
noEditor: boolean
@@ -46,12 +49,9 @@
const WM_DEPLOYERS_GROUP = 'wm_deployers'
let isDeployer = $derived($userStore?.groups?.includes(WM_DEPLOYERS_GROUP) ?? false)
let showPreserveToggle = $derived(
isDeployer &&
flowStore.val.on_behalf_of_email &&
$savedOnBehalfOfEmail &&
$savedOnBehalfOfEmail !== $userStore?.email
)
let canPreserve = $derived(!!$userStore?.is_admin || !!$userStore?.is_super_admin || isDeployer)
let onBehalfOfChoice: OnBehalfOfChoice = $state(undefined)
let customOnBehalfOfEmail: string = $state('')
function asSchema(x: any) {
return x as Schema
@@ -382,34 +382,54 @@
/>
<!-- On behalf of last editor section -->
<Toggle
textClass="font-medium"
size="xs"
checked={Boolean(flowStore.val.on_behalf_of_email)}
on:change={() => {
if (flowStore.val.on_behalf_of_email) {
flowStore.val.on_behalf_of_email = undefined
$preserveOnBehalfOf = false
} else {
flowStore.val.on_behalf_of_email = $userStore?.email
}
}}
options={{
right: 'Run on behalf of last editor',
rightTooltip:
'When this option is enabled, the flow will be run with the permissions of the last editor.'
}}
/>
{#if showPreserveToggle}
<span class="inline-flex gap-2">
<Toggle
textClass="font-medium"
size="xs"
bind:checked={$preserveOnBehalfOf}
checked={Boolean(flowStore.val.on_behalf_of_email)}
on:change={() => {
if (flowStore.val.on_behalf_of_email) {
flowStore.val.on_behalf_of_email = undefined
$preserveOnBehalfOf = false
onBehalfOfChoice = undefined
} else {
flowStore.val.on_behalf_of_email = $userStore?.email
}
}}
options={{
right: `Keep original author (${$savedOnBehalfOfEmail})`
right: `Run on behalf of ${canPreserve ? 'a specified user' : 'last editor'}`,
rightTooltip:
'When this option is enabled, the flow will be run with the permissions of the corresponding user.'
}}
/>
{/if}
{#if flowStore.val.on_behalf_of_email && canPreserve}
&rarr; <OnBehalfOfSelector
targetWorkspace={$workspaceStore ?? ''}
targetEmail={$savedOnBehalfOfEmail}
selected={onBehalfOfChoice}
onSelect={(choice, email) => {
onBehalfOfChoice = choice
if (choice === 'me') {
flowStore.val.on_behalf_of_email = $userStore?.email
customOnBehalfOfEmail = ''
$preserveOnBehalfOf = false
} else if (choice === 'target') {
flowStore.val.on_behalf_of_email = $savedOnBehalfOfEmail
customOnBehalfOfEmail = ''
$preserveOnBehalfOf = true
} else if (choice === 'custom' && email) {
flowStore.val.on_behalf_of_email = email
customOnBehalfOfEmail = email
$preserveOnBehalfOf = true
}
}}
kind="flow"
{canPreserve}
customEmail={customOnBehalfOfEmail}
isDeployment={false}
/>
{/if}
</span>
<!-- Error Handler Section -->
<div class="flex flex-row items-center py-1">
@@ -581,8 +601,8 @@
{#if flowStore.val.dedicated_worker}
<div class="mt-2">
<Alert type="info" title="Require dedicated workers">
A worker group needs to be configured to listen to this flow. Select it in the dedicated
workers section of the worker group configuration.
A worker group needs to be configured to listen to this flow. Select it in the
dedicated workers section of the worker group configuration.
</Alert>
</div>
{/if}