mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 08:02:18 +00:00
Add sticky note to saved flow data
This commit is contained in:
@@ -572,12 +572,12 @@ paths:
|
||||
use_case:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: Onboarding data submitted successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
type: string
|
||||
|
||||
/w/{workspace}/users/delete/{username}:
|
||||
delete:
|
||||
@@ -14963,8 +14963,7 @@ components:
|
||||
|
||||
CreatedAfterQueue:
|
||||
name: created_after_queue
|
||||
description:
|
||||
filter on jobs created after X for jobs in the queue only
|
||||
description: filter on jobs created after X for jobs in the queue only
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
@@ -14972,8 +14971,7 @@ components:
|
||||
|
||||
CreatedBeforeQueue:
|
||||
name: created_before_queue
|
||||
description:
|
||||
filter on jobs created before X for jobs in the queue only
|
||||
description: filter on jobs created before X for jobs in the queue only
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
@@ -15154,6 +15152,8 @@ components:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus"
|
||||
FlowStatusModule:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatusModule"
|
||||
FlowNote:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/FlowNote"
|
||||
# -- INLINE END --
|
||||
# Do not change line above
|
||||
|
||||
@@ -16419,30 +16419,30 @@ components:
|
||||
ScriptLang:
|
||||
type: string
|
||||
enum: [
|
||||
python3,
|
||||
deno,
|
||||
go,
|
||||
bash,
|
||||
powershell,
|
||||
postgresql,
|
||||
mysql,
|
||||
bigquery,
|
||||
snowflake,
|
||||
mssql,
|
||||
oracledb,
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
csharp,
|
||||
nu,
|
||||
java,
|
||||
ruby,
|
||||
duckdb,
|
||||
# for related places search: ADD_NEW_LANG
|
||||
]
|
||||
python3,
|
||||
deno,
|
||||
go,
|
||||
bash,
|
||||
powershell,
|
||||
postgresql,
|
||||
mysql,
|
||||
bigquery,
|
||||
snowflake,
|
||||
mssql,
|
||||
oracledb,
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
csharp,
|
||||
nu,
|
||||
java,
|
||||
ruby,
|
||||
duckdb,
|
||||
# for related places search: ADD_NEW_LANG
|
||||
]
|
||||
|
||||
Preview:
|
||||
type: object
|
||||
|
||||
@@ -782,8 +782,8 @@ async fn update_flow(
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE
|
||||
flow
|
||||
UPDATE
|
||||
flow
|
||||
SET
|
||||
path = $1,
|
||||
summary = $2,
|
||||
@@ -799,7 +799,7 @@ async fn update_flow(
|
||||
schema = $9::text::json,
|
||||
edited_by = $10,
|
||||
edited_at = now()
|
||||
WHERE
|
||||
WHERE
|
||||
path = $11 AND workspace_id = $12",
|
||||
if is_new_path { flow_path } else { &nf.path },
|
||||
nf.summary,
|
||||
@@ -823,8 +823,8 @@ async fn update_flow(
|
||||
if is_new_path {
|
||||
// if new path, must clone flow to new path and delete old flow for flow_version foreign key constraint
|
||||
sqlx::query!(
|
||||
"INSERT INTO flow
|
||||
(workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at)
|
||||
"INSERT INTO flow
|
||||
(workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at)
|
||||
SELECT workspace_id, $1, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at
|
||||
FROM flow
|
||||
WHERE path = $2 AND workspace_id = $3",
|
||||
@@ -1118,33 +1118,33 @@ async fn get_flow_by_path(
|
||||
let flow_o = if query.with_starred_info.unwrap_or(false) {
|
||||
sqlx::query_as::<_, FlowWithStarred>(
|
||||
r#"
|
||||
SELECT
|
||||
flow.workspace_id,
|
||||
flow.path,
|
||||
flow.lock_error_logs,
|
||||
flow.summary,
|
||||
flow.description,
|
||||
flow.archived,
|
||||
flow.extra_perms,
|
||||
flow.draft_only,
|
||||
flow.dedicated_worker,
|
||||
flow.tag,
|
||||
flow.ws_error_handler_muted,
|
||||
flow.timeout,
|
||||
flow.visible_to_runner_only,
|
||||
flow.on_behalf_of_email,
|
||||
flow_version.schema,
|
||||
flow_version.value,
|
||||
flow_version.created_at AS edited_at,
|
||||
SELECT
|
||||
flow.workspace_id,
|
||||
flow.path,
|
||||
flow.lock_error_logs,
|
||||
flow.summary,
|
||||
flow.description,
|
||||
flow.archived,
|
||||
flow.extra_perms,
|
||||
flow.draft_only,
|
||||
flow.dedicated_worker,
|
||||
flow.tag,
|
||||
flow.ws_error_handler_muted,
|
||||
flow.timeout,
|
||||
flow.visible_to_runner_only,
|
||||
flow.on_behalf_of_email,
|
||||
flow_version.schema,
|
||||
flow_version.value,
|
||||
flow_version.created_at AS edited_at,
|
||||
flow_version.created_by AS edited_by,
|
||||
favorite.path IS NOT NULL AS starred
|
||||
FROM flow
|
||||
LEFT JOIN favorite
|
||||
ON favorite.favorite_kind = 'flow'
|
||||
AND favorite.workspace_id = flow.workspace_id
|
||||
AND favorite.path = flow.path
|
||||
ON favorite.favorite_kind = 'flow'
|
||||
AND favorite.workspace_id = flow.workspace_id
|
||||
AND favorite.path = flow.path
|
||||
AND favorite.usr = $3
|
||||
LEFT JOIN flow_version
|
||||
LEFT JOIN flow_version
|
||||
ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
|
||||
WHERE flow.path = $1 AND flow.workspace_id = $2
|
||||
"#,
|
||||
@@ -1157,28 +1157,28 @@ async fn get_flow_by_path(
|
||||
} else {
|
||||
sqlx::query_as::<_, FlowWithStarred>(
|
||||
r#"
|
||||
SELECT
|
||||
flow.workspace_id,
|
||||
flow.path,
|
||||
flow.lock_error_logs,
|
||||
flow.summary,
|
||||
flow.description,
|
||||
flow.archived,
|
||||
flow.extra_perms,
|
||||
flow.draft_only,
|
||||
flow.dedicated_worker,
|
||||
flow.tag,
|
||||
flow.ws_error_handler_muted,
|
||||
flow.timeout,
|
||||
flow.visible_to_runner_only,
|
||||
flow.on_behalf_of_email,
|
||||
flow_version.schema,
|
||||
flow_version.value,
|
||||
flow_version.created_at AS edited_at,
|
||||
flow_version.created_by AS edited_by,
|
||||
SELECT
|
||||
flow.workspace_id,
|
||||
flow.path,
|
||||
flow.lock_error_logs,
|
||||
flow.summary,
|
||||
flow.description,
|
||||
flow.archived,
|
||||
flow.extra_perms,
|
||||
flow.draft_only,
|
||||
flow.dedicated_worker,
|
||||
flow.tag,
|
||||
flow.ws_error_handler_muted,
|
||||
flow.timeout,
|
||||
flow.visible_to_runner_only,
|
||||
flow.on_behalf_of_email,
|
||||
flow_version.schema,
|
||||
flow_version.value,
|
||||
flow_version.created_at AS edited_at,
|
||||
flow_version.created_by AS edited_by,
|
||||
NULL AS starred
|
||||
FROM flow
|
||||
LEFT JOIN flow_version
|
||||
LEFT JOIN flow_version
|
||||
ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
|
||||
WHERE flow.path = $1 AND flow.workspace_id = $2
|
||||
"#,
|
||||
@@ -1636,6 +1636,7 @@ mod tests {
|
||||
chat_input_enabled: None,
|
||||
debounce_key: None,
|
||||
debounce_delay_s: None,
|
||||
ui: None,
|
||||
};
|
||||
let expect = serde_json::json!({
|
||||
"modules": [
|
||||
|
||||
@@ -192,6 +192,29 @@ pub struct FlowValue {
|
||||
pub priority: Option<i16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub chat_input_enabled: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub notes: Option<Vec<FlowNote>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct FlowNote {
|
||||
pub id: String,
|
||||
pub text: String,
|
||||
pub position: FlowNotePosition,
|
||||
pub size: FlowNoteSize,
|
||||
pub color: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct FlowNotePosition {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct FlowNoteSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
impl FlowValue {
|
||||
|
||||
@@ -4149,6 +4149,7 @@ pub async fn push<'c, 'd>(
|
||||
skip_expr: None,
|
||||
preprocessor_module: None,
|
||||
chat_input_enabled: None,
|
||||
notes: None,
|
||||
};
|
||||
// this is a new flow being pushed, flow_status is set to flow_value:
|
||||
let flow_status: FlowStatus = FlowStatus::new(&flow_value);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { Flow } from '$lib/gen'
|
||||
import type { Flow, OpenFlow } from '$lib/gen'
|
||||
import { writable } from 'svelte/store'
|
||||
import { initFlowState, type FlowState } from './flowState'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
import type { ExtendedOpenFlow } from './types'
|
||||
|
||||
export type FlowMode = 'push' | 'pull'
|
||||
|
||||
@@ -11,24 +10,16 @@ export const importFlowStore = writable<Flow | undefined>(undefined)
|
||||
|
||||
export async function initFlow(
|
||||
flow: Flow,
|
||||
flowStore: StateStore<ExtendedOpenFlow>,
|
||||
flowStore: StateStore<Flow>,
|
||||
flowStateStore: StateStore<FlowState>
|
||||
) {
|
||||
await initFlowState(flow, flowStateStore)
|
||||
// Initialize ExtendedOpenFlow with ui.notes field if not present
|
||||
const extendedFlow: ExtendedOpenFlow = {
|
||||
...flow,
|
||||
ui: {
|
||||
notes: (flow as any).ui?.notes || [],
|
||||
...(flow as any).ui
|
||||
}
|
||||
}
|
||||
flowStore.val = extendedFlow
|
||||
flowStore.val = flow
|
||||
}
|
||||
|
||||
export async function copyFirstStepSchema(
|
||||
flowState: FlowState,
|
||||
flowStore: StateStore<ExtendedOpenFlow>
|
||||
flowStore: StateStore<OpenFlow>
|
||||
): Promise<void> {
|
||||
const firstModuleId = flowStore.val.value.modules[0]?.id
|
||||
|
||||
@@ -49,7 +40,7 @@ export async function copyFirstStepSchema(
|
||||
return sendUserToast('No first step found', true)
|
||||
}
|
||||
|
||||
export async function getFirstStepSchema(flowState: FlowState, flow: ExtendedOpenFlow) {
|
||||
export async function getFirstStepSchema(flowState: FlowState, flow: OpenFlow) {
|
||||
const firstModuleId = flow.value.modules[0]?.id
|
||||
|
||||
if (!firstModuleId || !flowState[firstModuleId]) {
|
||||
|
||||
@@ -426,12 +426,9 @@
|
||||
maxHeight={minHeight}
|
||||
modules={flowStore.val.value.modules}
|
||||
{noteMode}
|
||||
notes={flowStore.val.ui?.notes || []}
|
||||
notes={flowStore.val.value?.notes || []}
|
||||
onNotesChange={(newNotes) => {
|
||||
if (!flowStore.val.ui) {
|
||||
flowStore.val.ui = {}
|
||||
}
|
||||
flowStore.val.ui.notes = newNotes
|
||||
flowStore.val.value.notes = newNotes
|
||||
}}
|
||||
preprocessorModule={flowStore.val.value?.preprocessor_module}
|
||||
{selectedId}
|
||||
|
||||
@@ -15,6 +15,7 @@ import type ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
|
||||
import type { ModulesTestStates } from '../modulesTest.svelte'
|
||||
import type { ButtonProp } from '$lib/components/DiffEditor.svelte'
|
||||
|
||||
// Type for flow notes stored in the UI field
|
||||
export type Note = {
|
||||
id: string
|
||||
text: string
|
||||
@@ -36,6 +37,7 @@ export type FlowInput = Record<
|
||||
}
|
||||
>
|
||||
|
||||
// Extended OpenFlow with additional properties not in the core spec
|
||||
export type ExtendedOpenFlow = OpenFlow & {
|
||||
tag?: string
|
||||
ws_error_handler_muted?: boolean
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
JobService,
|
||||
type Flow,
|
||||
type FlowModule,
|
||||
type InputTransform,
|
||||
type Job,
|
||||
@@ -22,10 +21,11 @@ function create_context_function_template(eval_string: string, context: Record<s
|
||||
return `
|
||||
return function (context) {
|
||||
"use strict";
|
||||
${Object.keys(context).length > 0
|
||||
? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
|
||||
: ``
|
||||
}
|
||||
${
|
||||
Object.keys(context).length > 0
|
||||
? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
|
||||
: ``
|
||||
}
|
||||
return ${eval_string}
|
||||
}`
|
||||
}
|
||||
@@ -100,7 +100,7 @@ export function cleanInputs(flow: OpenFlow | any): OpenFlow & {
|
||||
visible_to_runner_only?: boolean
|
||||
on_behalf_of_email?: string
|
||||
} {
|
||||
const newFlow: Flow = JSON.parse(JSON.stringify(flow))
|
||||
const newFlow: OpenFlow = JSON.parse(JSON.stringify(flow))
|
||||
newFlow.value.modules.forEach((mod) => {
|
||||
if (mod.value.type == 'rawscript' || mod.value.type == 'script') {
|
||||
Object.values(mod.value.input_transforms ?? {}).forEach((inp) => {
|
||||
|
||||
@@ -69,6 +69,11 @@ components:
|
||||
chat_input_enabled:
|
||||
type: boolean
|
||||
description: Whether this flow accepts chat-style input
|
||||
notes:
|
||||
type: array
|
||||
description: Sticky notes attached to the flow
|
||||
items:
|
||||
$ref: "#/components/schemas/FlowNote"
|
||||
required:
|
||||
- modules
|
||||
|
||||
@@ -99,6 +104,52 @@ components:
|
||||
retry_if:
|
||||
$ref: "#/components/schemas/RetryIf"
|
||||
|
||||
FlowNote:
|
||||
type: object
|
||||
description: A sticky note attached to a flow for documentation and annotation
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Unique identifier for the note
|
||||
text:
|
||||
type: string
|
||||
description: Content of the note
|
||||
position:
|
||||
type: object
|
||||
description: Position of the note in the flow editor
|
||||
properties:
|
||||
x:
|
||||
type: number
|
||||
description: X coordinate
|
||||
y:
|
||||
type: number
|
||||
description: Y coordinate
|
||||
required:
|
||||
- x
|
||||
- y
|
||||
size:
|
||||
type: object
|
||||
description: Size of the note in the flow editor
|
||||
properties:
|
||||
width:
|
||||
type: number
|
||||
description: Width in pixels
|
||||
height:
|
||||
type: number
|
||||
description: Height in pixels
|
||||
required:
|
||||
- width
|
||||
- height
|
||||
color:
|
||||
type: string
|
||||
description: Color of the note (e.g., "yellow", "#ffff00")
|
||||
required:
|
||||
- id
|
||||
- text
|
||||
- position
|
||||
- size
|
||||
- color
|
||||
|
||||
RetryIf:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in New Issue
Block a user