Skip to content

Commit

Permalink
feat: add ability to edit id in flows (#4364)
Browse files Browse the repository at this point in the history
* all

* all

* nit mailto

* fix

* fix
  • Loading branch information
rubenfiszel authored Sep 10, 2024
1 parent 5dda5df commit a19db9a
Show file tree
Hide file tree
Showing 24 changed files with 298 additions and 145 deletions.
20 changes: 0 additions & 20 deletions backend/windmill-api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4737,26 +4737,6 @@ paths:
schema:
type: string

/w/{workspace}/flows/input_history/p/{path}:
get:
summary: list inputs for previous completed flow jobs
operationId: getFlowInputHistoryByPath
tags:
- flow
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
responses:
"200":
description: input history for completed jobs with this flow path
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Input"

/w/{workspace}/raw_apps/list:
get:
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/components/FlowMetadata.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@
{#if job.is_flow_step}
<div class="flex flex-row gap-2 items-center text-sm">
<BarsStaggered size={SMALL_ICON_SIZE} class="text-secondary min-w-3.5" />
<span class="whitespace-nowrap">
<span class="whitespace-nowrap text-sm">
Step of flow
<a href={`${base}/run/${job.parent_job}?workspace=${$workspaceStore}`}>
{job.parent_job}
{truncateRev(job.parent_job, 18)}
</a>
</span>
</div>
Expand Down
85 changes: 85 additions & 0 deletions frontend/src/lib/components/IdEditorInput.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<script lang="ts">
import { ArrowRight } from 'lucide-svelte'
import { Button } from './common'
import { createEventDispatcher } from 'svelte'
import { forbiddenIds } from './flows/idUtils'
import { slide } from 'svelte/transition'
export let initialId: string
export let reservedIds: string[] = []
export let label: string = 'Component ID'
export let value = initialId
export let buttonText = ''
export let btnClasses = '!p-1 !w-[34px] !ml-1'
let error = ''
const dispatch = createEventDispatcher()
const regex = /^[a-zA-Z][a-zA-Z0-9]*$/
$: validateId(value, reservedIds)
function validateId(id: string, reservedIds: string[]) {
if (id == initialId) {
error = ''
return
}
if (!regex.test(value)) {
error = 'The ID must include only letters and numbers and start with a letter'
} else if (forbiddenIds.includes(value)) {
error = 'This ID is reserved'
} else if (reservedIds.some((rid) => rid === value)) {
error = 'This ID is already in use'
} else {
error = ''
}
}
let inputDiv: HTMLInputElement | undefined = undefined
$: inputDiv?.focus()
</script>

<label class="block text-primary">
{#if label != ''}
<div class="pb-1 text-sm text-secondary">{label}</div>
{/if}
<div class="flex w-full">
<input
bind:this={inputDiv}
autofocus
type="text"
bind:value
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation={({ key }) => {
if (key === 'Enter' && error === '' && value !== initialId) {
dispatch('save', value)
} else if (key == 'Escape') {
dispatch('close')
}
}}
on:keypress|stopPropagation
/>
<Button
size="xs"
color="blue"
buttonType="button"
{btnClasses}
aria-label="Save ID"
disabled={error != '' || value === initialId}
on:click={() => {
dispatch('save', value)
}}
>
{buttonText}<ArrowRight size={18} />
</Button>
</div>
{#if error != ''}
<div
transition:slide|local={{ duration: 100 }}
class="w-full text-sm text-red-600 whitespace-pre-wrap pt-1"
>
{error}
</div>
{/if}
</label>
Original file line number Diff line number Diff line change
@@ -1,40 +1,18 @@
<script lang="ts">
import type { AppViewerContext } from '$lib/components/apps/types'
import { allItems } from '$lib/components/apps/utils'
import { forbiddenIds } from '$lib/components/flows/idUtils'
import { ArrowRight, Pencil } from 'lucide-svelte'
import { Pencil } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { slide } from 'svelte/transition'
import { Button, Popup } from '../../../../common'
import IdEditorInput from '$lib/components/IdEditorInput.svelte'
import { Popup } from '$lib/components/common'
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
export let id: string
const dispatch = createEventDispatcher()
const regex = /^[a-zA-Z][a-zA-Z0-9]*$/
let value = id
let error = ''
$: if (!regex.test(value)) {
error = 'The ID must include only letters and numbers and start with a letter'
} else if (forbiddenIds.includes(value)) {
error = 'This ID is reserved'
} else if (
allItems($app.grid, $app.subgrids).some((item) => item.id === value && item.id !== id)
) {
error = 'This ID is already in use'
} else {
error = ''
}
const dispatch = createEventDispatcher()
function save() {
if (error != '') {
return
}
if (value != id) {
dispatch('change', value)
}
}
$: reservedIds = allItems($app.grid, $app.subgrids).map((item) => item.id)
</script>

<Popup let:close floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}>
Expand All @@ -50,44 +28,13 @@
<Pencil size={14} />
</button>
</svelte:fragment>
<label class="block text-primary">
<div class="pb-1 text-sm text-secondary">Component ID</div>
<div class="flex w-full">
<input
type="text"
bind:value
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation
on:keypress|stopPropagation={({ key }) => {
if (key === 'Enter') {
save()
close(null)
}
}}
/>
<Button
size="xs"
color="blue"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Save ID"
disabled={error != ''}
on:click={() => {
save()
close(null)
}}
>
<ArrowRight size={18} />
</Button>
</div>
{#if error != ''}
<div
transition:slide|local={{ duration: 100 }}
class="w-full text-sm text-red-600 whitespace-pre-wrap pt-1"
>
{error}
</div>
{/if}
</label>
<IdEditorInput
initialId={id}
on:close={() => close(null)}
on:save={(e) => {
dispatch('save', e.detail)
close(null)
}}
{reservedIds}
/>
</Popup>
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
style="width: 275px; height: 34px; background-color: {getStateColor(
undefined,
darkMode,
'#fff'
true
)};"
on:click={() => {
selected = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
dispatch('node')
}}
type="button"
class="text-primary bg-surface border-[1px] mx-[1px] border-gray-300 dark:border-gray-500 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center"
class="text-primary bg-surface outline-[1px] outline dark:outline-gray-500 outline-gray-300 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center"
>
<Cross class="mx-[5px]" size={15} />
</button>
Expand All @@ -28,7 +28,7 @@
type="button"
on:click={() => dispatch('addBranch')}
class={twMerge(
'text-primary bg-surface border-[1px] mx-[1px] border-gray-300 dark:border-gray-500 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center',
'text-secondary bg-surface outline-[1px] outline dark:outline-gray-500 outline-gray-300 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center',
!canAddNode && 'ml-16 mb-2'
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<div
class={classNames(
'fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity',
open ? 'ease-out duration-300 opacity-80' : 'ease-in duration-200 opacity-0'
open ? 'ease-out duration-300 opacity-100' : 'ease-in duration-200 opacity-0'
)}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
>
Single JavaScript expression. The following functions and objects are available:
<ul class="ml-4">
<li><b>{'results.<id>'}</b>: the result of step at id 'id'</li>
<li
><b>{'results.<id>'}</b>: the result of step at id 'id' (use <b>{'results?.<id>'}</b> if id may
not exist because branch was not chosen)</li
>
<li><b>flow_input</b>: the object containing the flow input arguments</li>
<li><b>params</b>: the object containing the current step static values</li>
<li>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/flows/flowModuleNextId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { charsToNumber, numberToChars } from './idUtils'
export function nextId(flowState: FlowState, fullFlow: OpenFlow): string {
const allIds = dfs(fullFlow.value.modules, (fm) => fm.id)
const max = allIds.concat(Object.keys(flowState)).reduce((acc, key) => {
if (key === 'failure' || key.includes('branch') || key.includes('loop')) {
if (key.length >= 4) {
return acc
} else {
const num = charsToNumber(key)
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/lib/components/flows/flowStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ export async function copyFirstStepSchema(flowState: FlowState, flowStore: Writa
return flow
})
}

export function replaceId(expr: string, id: string, newId: string): string {
return expr
.replaceAll(`results.${id}`, `results.${newId}`)
.replaceAll(`results?.${id}`, `results?.${newId}`)
}
Loading

0 comments on commit a19db9a

Please sign in to comment.