diff --git a/backend/migrations/20220719101639_script_trigger_2.down.sql b/backend/migrations/20220719101639_script_trigger_2.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20220719101639_script_trigger_2.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20220719101639_script_trigger_2.up.sql b/backend/migrations/20220719101639_script_trigger_2.up.sql new file mode 100644 index 0000000000..5d53c35b04 --- /dev/null +++ b/backend/migrations/20220719101639_script_trigger_2.up.sql @@ -0,0 +1,4 @@ +-- Add up migration script here +ALTER TABLE script DROP COLUMN trigger_reco_interval; +ALTER TABLE script ADD COLUMN is_trigger BOOLEAN NOT NULL DEFAULT false; + diff --git a/backend/openapi.yaml b/backend/openapi.yaml index e285ca3e5b..bf39c24056 100644 --- a/backend/openapi.yaml +++ b/backend/openapi.yaml @@ -1609,6 +1609,15 @@ paths: in: query schema: type: boolean + - name: is_trigger + description: | + (default regardless) + if true show only the trigger scripts + if false show only the non trigger scripts + if not defined, show all regardless of if the script is a trigger script + in: query + schema: + type: boolean responses: "200": description: All available scripts @@ -1656,8 +1665,8 @@ paths: language: type: string enum: [python3, deno] - trigger_reco_interval: - type: integer + is_trigger: + type: boolean required: - path - summary @@ -2266,7 +2275,11 @@ paths: - $ref: "#/components/parameters/CreatedAfter" - $ref: "#/components/parameters/Success" - $ref: "#/components/parameters/JobKinds" - + - name: is_skipped + description: is the job skipped + in: query + schema: + type: boolean responses: "200": description: All available completed jobs @@ -2293,7 +2306,11 @@ paths: - $ref: "#/components/parameters/CreatedBefore" - $ref: "#/components/parameters/CreatedAfter" - $ref: "#/components/parameters/JobKinds" - + - name: is_skipped + description: is the job skipped + in: query + schema: + type: boolean - name: success description: filter on successful jobs in: query @@ -3115,8 +3132,8 @@ components: language: type: string enum: [python3, deno] - trigger_reco_interval: - type: integer + is_trigger: + type: boolean required: - hash - path @@ -3129,6 +3146,7 @@ components: - is_template - extra_perms - language + - is_trigger ScriptArgs: type: object diff --git a/backend/src/jobs.rs b/backend/src/jobs.rs index ad6bff43ee..7903a67561 100644 --- a/backend/src/jobs.rs +++ b/backend/src/jobs.rs @@ -557,6 +557,7 @@ pub struct ListCompletedQuery { pub parent_job: Option, pub order_desc: Option, pub job_kinds: Option, + pub is_skipped: Option, } fn list_completed_jobs_query( w_id: &str, @@ -597,6 +598,9 @@ fn list_completed_jobs_query( if let Some(dt) = &lq.created_after { sqlb.and_where_gt("created_at", format!("to_timestamp({})", dt.timestamp())); } + if let Some(sk) = &lq.is_skipped { + sqlb.and_where_eq("is_skipped", sk); + } if let Some(jk) = &lq.job_kinds { sqlb.and_where_in( "job_kind", diff --git a/backend/src/scripts.rs b/backend/src/scripts.rs index e0b6dae599..6b2cfa08ee 100644 --- a/backend/src/scripts.rs +++ b/backend/src/scripts.rs @@ -129,7 +129,7 @@ pub struct Script { pub lock: Option, pub lock_error_logs: Option, pub language: ScriptLang, - pub trigger_reco_interval: Option, + pub is_trigger: bool, } #[derive(Serialize, Deserialize, sqlx::Type, Debug)] @@ -156,7 +156,7 @@ pub struct NewScript { pub is_template: Option, pub lock: Option>, pub language: ScriptLang, - pub trigger_reco_interval: Option, + pub is_trigger: Option, } #[derive(Deserialize)] @@ -171,6 +171,7 @@ pub struct ListScriptQuery { pub order_by: Option, pub order_desc: Option, pub is_template: Option, + pub is_trigger: Option, } async fn list_scripts( @@ -201,7 +202,7 @@ async fn list_scripts( "null as lock", "CASE WHEN lock_error_logs IS NOT NULL THEN 'error' ELSE null END as lock_error_logs", "language", - "trigger_reco_interval", + "is_trigger", ]) .order_by("created_at", lq.order_desc.unwrap_or(true)) .and_where("workspace_id = ? OR workspace_id = 'starter'".bind(&w_id)) @@ -239,6 +240,9 @@ async fn list_scripts( if let Some(it) = &lq.is_template { sqlb.and_where_eq("is_template", it); } + if let Some(it) = &lq.is_trigger { + sqlb.and_where_eq("is_trigger", it); + } let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; @@ -411,7 +415,7 @@ async fn create_script( //::text::json is to ensure we use serde_json with preserve order sqlx::query!( "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, \ - created_by, schema, is_template, extra_perms, lock, language, trigger_reco_interval) VALUES \ + created_by, schema, is_template, extra_perms, lock, language, is_trigger) VALUES \ ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14)", &w_id, &hash.0, @@ -426,7 +430,7 @@ async fn create_script( extra_perms, lock, ns.language: ScriptLang, - ns.trigger_reco_interval, + ns.is_trigger.unwrap_or(false), ) .execute(&mut tx) .await?; diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 23f94010de..b2a5fbe506 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -20,7 +20,7 @@ export let contentEncoding = '' export let type: string | undefined = undefined export let required = false - export let pattern: undefined | string + export let pattern: undefined | string = undefined export let valid = required ? false : true export let minRows = 1 export let maxRows = 10 diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index 227cbffa74..b750413e9a 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -3,11 +3,11 @@ import { page } from '$app/stores' import { FlowService, ScriptService, type Flow } from '$lib/gen' import { clearPreviewResults, hubScripts, workspaceStore } from '$lib/stores' - import { pathIsEmpty, sendUserToast } from '$lib/utils' + import { sendUserToast, setQueryWithoutLoad } from '$lib/utils' import { onMount } from 'svelte' import SvelteMarkdown from 'svelte-markdown' import FlowEditor from './FlowEditor.svelte' - import { flowStore, initFlow, type FlowMode } from './flows/flowStore' + import { flowStore, type FlowMode } from './flows/flowStore' import { flowToMode } from './flows/utils' import Path from './Path.svelte' import Required from './Required.svelte' @@ -68,12 +68,9 @@ } flowStore.subscribe((flow: Flow) => { - $page.url.searchParams.set('state', btoa(JSON.stringify(flowToMode(flow, mode)))) - history.replaceState({}, '', $page.url) + setQueryWithoutLoad($page.url, 'state', btoa(JSON.stringify(flowToMode(flow, mode)))) }) - $: flow && initFlow(flow) - onMount(() => { loadSearchData() clearPreviewResults() diff --git a/frontend/src/lib/components/Path.svelte b/frontend/src/lib/components/Path.svelte index 2d9d900785..d46511ea9f 100644 --- a/frontend/src/lib/components/Path.svelte +++ b/frontend/src/lib/components/Path.svelte @@ -66,31 +66,33 @@ let validateTimeout: NodeJS.Timeout | undefined = undefined async function validatePath(path: string, kind: PathKind): Promise { - if (initialPath == '' && initialPath != path) { + if (initialPath != '' && initialPath != path) { if (validateTimeout) { clearTimeout(validateTimeout) } validateTimeout = setTimeout(async () => { if ( - (kind == 'flow' && + initialPath != '' && + initialPath != path && + ((kind == 'flow' && (await FlowService.existsFlowByPath({ workspace: $workspaceStore!, path: path }))) || - (kind == 'script' && - (await ScriptService.existsScriptByPath({ - workspace: $workspaceStore!, - path: path - }))) || - (kind == 'resource' && - (await ResourceService.existsResource({ - workspace: $workspaceStore!, - path: path - }))) || - (kind == 'variable' && - (await VariableService.existsVariable({ - workspace: $workspaceStore!, - path: path - }))) || - (kind == 'schedule' && - (await ScheduleService.existsSchedule({ workspace: $workspaceStore!, path: path }))) + (kind == 'script' && + (await ScriptService.existsScriptByPath({ + workspace: $workspaceStore!, + path: path + }))) || + (kind == 'resource' && + (await ResourceService.existsResource({ + workspace: $workspaceStore!, + path: path + }))) || + (kind == 'variable' && + (await VariableService.existsVariable({ + workspace: $workspaceStore!, + path: path + }))) || + (kind == 'schedule' && + (await ScheduleService.existsSchedule({ workspace: $workspaceStore!, path: path })))) ) { error = 'path already used' } else if (validateName(meta)) { diff --git a/frontend/src/lib/components/SchemaModal.svelte b/frontend/src/lib/components/SchemaModal.svelte index 06204e4e45..c3d197aba5 100644 --- a/frontend/src/lib/components/SchemaModal.svelte +++ b/frontend/src/lib/components/SchemaModal.svelte @@ -61,7 +61,7 @@ export let property: ModalSchemaProperty = DEFAULT_PROPERTY export let error = '' export let editing = false - export let oldArgName: string | undefined + export let oldArgName: string | undefined = undefined const dispatch = createEventDispatcher() let modal: Modal diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 289c616fa8..7fb72454fd 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -1,7 +1,7 @@ - diff --git a/frontend/src/lib/components/flows/pickers/PickScript.svelte b/frontend/src/lib/components/flows/pickers/PickScript.svelte index 368671b40b..c6ceef96fd 100644 --- a/frontend/src/lib/components/flows/pickers/PickScript.svelte +++ b/frontend/src/lib/components/flows/pickers/PickScript.svelte @@ -17,7 +17,7 @@ const dispatch = createEventDispatcher() async function loadItems(): Promise { - items = await ScriptService.listScripts({ workspace: $workspaceStore! }) + items = await ScriptService.listScripts({ workspace: $workspaceStore!, isTrigger }) } $: { diff --git a/frontend/src/lib/components/flows/utils.ts b/frontend/src/lib/components/flows/utils.ts index 30f725f1b0..6d6e6a20ef 100644 --- a/frontend/src/lib/components/flows/utils.ts +++ b/frontend/src/lib/components/flows/utils.ts @@ -8,7 +8,6 @@ import { } from '$lib/gen' import { inferArgs } from '$lib/infer' import { loadSchema } from '$lib/scripts' -import { DENO_INIT_CODE, PYTHON_INIT_CODE } from '$lib/script_helpers' import { workspaceStore } from '$lib/stores' import { emptySchema } from '$lib/utils' import { get } from 'svelte/store' @@ -121,16 +120,6 @@ export async function getFirstStepSchema(flow: Flow): Promise { return emptySchema() } -export function createInlineScriptModule(language: FlowModuleValue.language): FlowModuleValue { - const code = language === FlowModuleValue.language.DENO ? DENO_INIT_CODE : PYTHON_INIT_CODE - - return { - type: FlowModuleValue.type.RAWSCRIPT, - content: code, - language - } -} - export async function getScriptByPath(path: string): Promise<{ content: string language: FlowModuleValue.language diff --git a/frontend/src/lib/script_helpers.ts b/frontend/src/lib/script_helpers.ts index 96786268ba..6405bb359c 100644 --- a/frontend/src/lib/script_helpers.ts +++ b/frontend/src/lib/script_helpers.ts @@ -33,11 +33,6 @@ def main(name: str = "Nicolas Bourbaki", return {"version": version, "splitted": name.split(), "user": user} ` -export const PYTHON_INIT_CODE_TRIGGER = `import wmill - -def main(): - return [1,2,3] -` export const DENO_INIT_CODE = `// only do the following import if you require your script to interact with the windmill // for instance to get a variable or resource // import * as wmill from 'https://deno.land/x/windmill@v${__pkg__.version}/mod.ts' @@ -68,3 +63,9 @@ export async function main() { return [1,2,3] } ` + +export function initialCode(language: 'deno' | 'python3', is_trigger: boolean): string { + return language === 'deno' ? + (is_trigger ? DENO_INIT_CODE_TRIGGER : DENO_INIT_CODE) : PYTHON_INIT_CODE + +} diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 360c9a3fc4..f61fff737d 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { goto } from '$app/navigation' import type { User } from '$lib/gen' import { toast } from '@zerodevx/svelte-toast' import type { Schema } from './common' @@ -194,6 +195,17 @@ export function pathIsEmpty(path: string): boolean { return path == undefined || path.split('/')[2] == '' } +export async function setQuery(url: URL, key: string, value: string): Promise { + url.searchParams.set(key, value) + await goto(`?${url.searchParams.toString()}`) +} + +export async function setQueryWithoutLoad(url: URL, key: string, value: string): Promise { + const nurl = new URL(url.toString()) + nurl.searchParams.set(key, value) + history.replaceState(null, '', nurl.toString()) +} + export function groupBy( scripts: T[], toGroup: (t: T) => string, diff --git a/frontend/src/routes/flows/add.svelte b/frontend/src/routes/flows/add.svelte index 228f51e893..bb4408e61c 100644 --- a/frontend/src/routes/flows/add.svelte +++ b/frontend/src/routes/flows/add.svelte @@ -2,6 +2,7 @@ import { page } from '$app/stores' import FlowBuilder from '$lib/components/FlowBuilder.svelte' + import { initFlow } from '$lib/components/flows/flowStore' import type { Flow } from '$lib/gen' import { emptySchema } from '$lib/utils' @@ -20,6 +21,8 @@ extra_perms: {}, schema: emptySchema() } + + initFlow(flow) diff --git a/frontend/src/routes/flows/edit/[...path].svelte b/frontend/src/routes/flows/edit/[...path].svelte index 0dd69f6d9e..dbb76ac119 100644 --- a/frontend/src/routes/flows/edit/[...path].svelte +++ b/frontend/src/routes/flows/edit/[...path].svelte @@ -6,6 +6,7 @@ import { workspaceStore } from '$lib/stores' import { emptySchema } from '$lib/utils' import { flattenForloopFlows } from '$lib/components/flows/utils' + import { initFlow } from '$lib/components/flows/flowStore' const initialState = $page.url.searchParams.get('state') let flowLoadedFromUrl = initialState != undefined ? JSON.parse(atob(initialState)) : undefined @@ -33,6 +34,7 @@ }) flow = flattenForloopFlows(flow) initialPath = flow.path + initFlow(flow) } $: { diff --git a/frontend/src/routes/runs/[...path].svelte b/frontend/src/routes/runs/[...path].svelte index 507f0a3193..ad400e52ab 100644 --- a/frontend/src/routes/runs/[...path].svelte +++ b/frontend/src/routes/runs/[...path].svelte @@ -1,7 +1,7 @@