From 6315ace3432bff4c1b4d530aad97f3c7014501fb Mon Sep 17 00:00:00 2001 From: dieriba Date: Wed, 13 Nov 2024 23:46:10 +0100 Subject: [PATCH 01/50] feat: init database triggers --- backend/windmill-api/src/database_triggers.rs | 41 +++++++++++++++++++ backend/windmill-api/src/lib.rs | 3 +- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 backend/windmill-api/src/database_triggers.rs diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs new file mode 100644 index 0000000000000..29c22773d4968 --- /dev/null +++ b/backend/windmill-api/src/database_triggers.rs @@ -0,0 +1,41 @@ +use axum::{extract::{Path, Query}, Extension, Router}; +use http::StatusCode; +use serde::Deserialize; +use windmill_common::{db::UserDB, error}; + +use crate::db::ApiAuthed; + +#[derive(Deserialize)] +struct DatabaseTrigger {} + +#[derive(Deserialize)] +pub struct ListDatabaseTriggerQuery { + pub page: Option, + pub per_page: Option, + pub path: Option, + pub is_flow: Option, + pub path_start: Option, +} + +async fn create_database_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, +) -> error::Result<(StatusCode, String)> { + todo!() +} + +async fn list_database_triggers( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, + Query(lst): Query, +) -> error::JsonResult> { + todo!() +} + +pub fn workspaced_service() -> Router { + Router::new() + .route("/create", create_database_trigger) + .nest("/list", list_database_triggers) +} diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 5a7f7fcded243..507569075b837 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -52,6 +52,7 @@ mod concurrency_groups; mod configs; mod db; mod drafts; +mod database_triggers; pub mod ee; pub mod embeddings; mod favorite; @@ -298,7 +299,7 @@ pub async fn run_server( .nest( "/websocket_triggers", websocket_triggers::workspaced_service(), - ), + ).nest("/database_triggers", database_triggers::workspaced_service()), ) .nest("/workspaces", workspaces::global_service()) .nest( From aa298c8459f2ece228b7a96337980a065958d41b Mon Sep 17 00:00:00 2001 From: dieriba Date: Sun, 17 Nov 2024 20:51:23 +0100 Subject: [PATCH 02/50] feat: :sparkles: wip: database_triggers --- backend/Cargo.lock | 12 +++++ backend/Cargo.toml | 2 +- backend/windmill-api/src/database_triggers.rs | 51 ++++++++++++++++--- .../components/sidebar/SidebarContent.svelte | 18 +++++-- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 6e7053f97d52e..aff65d27402ac 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1038,6 +1038,7 @@ checksum = "49c41b948da08fb481a94546cd874843adc1142278b0af4badf9b1b78599d68d" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bytes", "futures-util", "http 1.1.0", @@ -1086,6 +1087,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "backtrace" version = "0.3.74" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index ff795b3954496..f36c0deeddd16 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -134,7 +134,7 @@ windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" } windmill-parser-php = { path = "./parsers/windmill-parser-php" } windmill-api-client = { path = "./windmill-api-client" } -axum = { version = "^0.7", features = ["multipart"] } +axum = { version = "^0.7", features = ["multipart", "macros"] } headers = "^0" hyper = { version = "^1", features = ["full"] } tokio = { version = "^1", features = ["full", "tracing"] } diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs index 29c22773d4968..d314a48bbb9f5 100644 --- a/backend/windmill-api/src/database_triggers.rs +++ b/backend/windmill-api/src/database_triggers.rs @@ -1,14 +1,21 @@ -use axum::{extract::{Path, Query}, Extension, Router}; +use axum::{ + extract::{Path, Query}, + routing::{delete, get, post}, + Extension, Json, Router, +}; use http::StatusCode; -use serde::Deserialize; -use windmill_common::{db::UserDB, error}; +use serde::{Deserialize, Serialize}; +use windmill_common::{db::UserDB, error, utils::StripPath}; use crate::db::ApiAuthed; #[derive(Deserialize)] +struct EditDatabaseTrigger {} + +#[derive(Deserialize, Serialize)] struct DatabaseTrigger {} -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] pub struct ListDatabaseTriggerQuery { pub page: Option, pub per_page: Option, @@ -22,7 +29,7 @@ async fn create_database_trigger( Extension(user_db): Extension, Path(w_id): Path, ) -> error::Result<(StatusCode, String)> { - todo!() + Ok((StatusCode::OK, "OK".to_string())) } async fn list_database_triggers( @@ -31,11 +38,39 @@ async fn list_database_triggers( Path(w_id): Path, Query(lst): Query, ) -> error::JsonResult> { - todo!() + Ok(Json(Vec::new())) +} + +async fn get_database_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> error::JsonResult { + Ok(Json(DatabaseTrigger {})) +} + +async fn update_database_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Json(ct): Json, +) -> error::Result { + Ok(String::new()) +} + +async fn delete_database_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> error::Result { + Ok(format!("Databse trigger deleted")) } pub fn workspaced_service() -> Router { Router::new() - .route("/create", create_database_trigger) - .nest("/list", list_database_triggers) + .route("/create", post(create_database_trigger)) + .route("/list", get(list_database_triggers)) + .route("/get/*path", get(get_database_trigger)) + .route("/update/*path", post(update_database_trigger)) + .route("/delete/*path", delete(delete_database_trigger)) } diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index 4e340712560c6..f8cf3505c5e23 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -31,7 +31,8 @@ UserCog, Plus, Unplug, - AlertCircle + AlertCircle, + Database } from 'lucide-svelte' import Menu from '../common/menu/MenuV2.svelte' import MenuButton from './MenuButton.svelte' @@ -97,6 +98,13 @@ icon: Unplug, disabled: $userStore?.operator, kind: 'ws' + }, + { + label: 'Database', + href: '/database_triggers', + icon: Database, + disabled: $userStore?.operator, + kind: 'database' } ] @@ -205,10 +213,10 @@ action: () => { isCriticalAlertsUIOpen.set(true) }, - icon: AlertCircle, - notificationCount: numUnacknowledgedCriticalAlerts - } - ] + icon: AlertCircle, + notificationCount: numUnacknowledgedCriticalAlerts + } + ] : []) ] } From 80cc8e05242dbe37110430afc4d71d3052afd675 Mon Sep 17 00:00:00 2001 From: dieriba Date: Sun, 17 Nov 2024 20:51:23 +0100 Subject: [PATCH 03/50] feat: :sparkles: add database triggers front view --- .../components/sidebar/SidebarContent.svelte | 1 - .../(logged)/database_triggers/+page.js | 5 + .../(logged)/database_triggers/+page.svelte | 424 ++++++++++++++++++ 3 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 frontend/src/routes/(root)/(logged)/database_triggers/+page.js create mode 100644 frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index f8cf3505c5e23..9386ed082df9b 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -332,7 +332,6 @@ {#if subItem.icon} {/if} - {subItem.label} diff --git a/frontend/src/routes/(root)/(logged)/database_triggers/+page.js b/frontend/src/routes/(root)/(logged)/database_triggers/+page.js new file mode 100644 index 0000000000000..8e3a86c5f42d6 --- /dev/null +++ b/frontend/src/routes/(root)/(logged)/database_triggers/+page.js @@ -0,0 +1,5 @@ +export function load() { + return { + stuff: { title: 'WS triggers' } + } +} diff --git a/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte new file mode 100644 index 0000000000000..5fda52b525163 --- /dev/null +++ b/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte @@ -0,0 +1,424 @@ + + + + + (x.summary ?? '') + ' ' + x.path + ' (' + x.script_path + ')'} +/> + + + + + + + {#if isCloudHosted()} + + Database triggers are disabled in the multi-tenant cloud. + +
+ {/if} +
+
+ +
+
Filter by path of
+ + + + +
+ + +
+ {#if $userStore?.is_super_admin && $userStore.username.includes('@')} + + {:else if $userStore?.is_admin || $userStore?.is_super_admin} + + {/if} +
+
+ {#if loading} + {#each new Array(6) as _} + + {/each} + {:else if !triggers?.length} +
No database triggers
+ {:else if items?.length} +
+ {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, url, is_flow, extra_perms, canWrite, marked, error, last_server_ping, enabled } (path)} + {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} + {@const ping = last_server_ping ? new Date(last_server_ping) : undefined} + +
+
+ + + websocketTriggerEditor?.openEdit(path, is_flow)} + class="min-w-0 grow hover:underline decoration-gray-400" + > +
+ {#if marked} + + {@html marked} + + {:else} + {url.startsWith('$script:') + ? 'URL: ' + url.replace('$script:', 'result of script ') + : url.startsWith('$flow:') + ? 'URL: ' + url.replace('$flow:', 'result of flow ') + : url} + {/if} +
+
+ {path} +
+
+ runnable: {script_path} +
+
+ + + +
+ {#if (enabled && (!ping || ping.getTime() < new Date().getTime() - 15 * 1000 || error)) || (!enabled && error)} + + + + + +
+ {#if enabled} + Websocket is not connected{error ? ': ' + error : ''} + {:else} + Websocket was disabled because of an error: {error} + {/if} +
+
+ {:else if enabled} + + + + +
Websocket is connected
+
+ {/if} +
+ + { + setTriggerEnabled(path, e.detail) + }} + /> + +
+ + { + goto(href) + } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: !canWrite, + action: async () => { + await WebsocketTriggerService.deleteWebsocketTrigger({ + workspace: $workspaceStore ?? '', + path + }) + loadTriggers() + } + }, + { + displayName: canWrite ? 'Edit' : 'View', + icon: canWrite ? Pen : Eye, + action: () => { + websocketTriggerEditor?.openEdit(path, is_flow) + } + }, + { + displayName: 'Audit logs', + icon: Eye, + href: `${base}/audit_logs?resource=${path}` + }, + { + displayName: canWrite ? 'Share' : 'See Permissions', + icon: Share, + action: () => { + shareModal.openDrawer(path, 'websocket_trigger') + } + } + ]} + /> +
+
+
+
edited by {edited_by}
the {displayDate(edited_at)}
+
+ {/each} +
+ {:else} + + {/if} +
+ {#if items && items?.length > 15 && nbDisplayed < items.length} + {nbDisplayed} items out of {items.length} + + {/if} + + + { + loadTriggers() + }} +/> From 3e367e4f7aa029e9ae2057c2c28fcdf4878b2cc0 Mon Sep 17 00:00:00 2001 From: dieriba Date: Sun, 17 Nov 2024 20:51:23 +0100 Subject: [PATCH 04/50] feat: :construction: database_triggers --- backend/windmill-api/src/database_triggers.rs | 34 +- .../src/lib/components/FlowBuilder.svelte | 3 +- .../src/lib/components/ScriptBuilder.svelte | 2 +- .../details/DetailPageDetailPanel.svelte | 2 + .../details/DetailPageLayout.svelte | 11 +- .../details/DetailPageTriggerPanel.svelte | 9 + .../renderers/triggers/TriggersBadge.svelte | 7 +- frontend/src/lib/components/graph/util.ts | 2 +- frontend/src/lib/components/triggers.ts | 2 +- .../components/triggers/TriggersEditor.svelte | 16 +- .../DatabaseTriggerEditor.svelte | 23 + .../DatabaseTriggerEditorInner.svelte | 634 ++++++++++++++++++ .../DatabaseTriggersPanel.svelte | 106 +++ .../{ => http_triggers}/RouteEditor.svelte | 0 .../RouteEditorInner.svelte | 16 +- .../{ => http_triggers}/RoutesPanel.svelte | 10 +- .../ScheduledPollPanel.svelte | 0 .../WebhooksPanel.svelte | 8 +- .../WebsocketTriggerEditor.svelte | 0 .../WebsocketTriggerEditorInner.svelte | 8 +- .../WebsocketTriggersPanel.svelte | 6 +- .../(logged)/database_triggers/+page.svelte | 14 +- .../(logged)/flows/get/[...path]/+page.svelte | 12 +- .../(root)/(logged)/routes/+page.svelte | 2 +- .../scripts/get/[...hash]/+page.svelte | 12 +- .../(logged)/websocket_triggers/+page.svelte | 2 +- 26 files changed, 887 insertions(+), 54 deletions(-) create mode 100644 frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditor.svelte create mode 100644 frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte create mode 100644 frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte rename frontend/src/lib/components/triggers/{ => http_triggers}/RouteEditor.svelte (100%) rename frontend/src/lib/components/triggers/{ => http_triggers}/RouteEditorInner.svelte (96%) rename frontend/src/lib/components/triggers/{ => http_triggers}/RoutesPanel.svelte (92%) rename frontend/src/lib/components/triggers/{ => scheduled_triggers}/ScheduledPollPanel.svelte (100%) rename frontend/src/lib/components/triggers/{ => webhook_triggers}/WebhooksPanel.svelte (98%) rename frontend/src/lib/components/triggers/{ => websocket_triggers}/WebsocketTriggerEditor.svelte (100%) rename frontend/src/lib/components/triggers/{ => websocket_triggers}/WebsocketTriggerEditorInner.svelte (98%) rename frontend/src/lib/components/triggers/{ => websocket_triggers}/WebsocketTriggersPanel.svelte (93%) diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs index d314a48bbb9f5..2d50f63a5f9b0 100644 --- a/backend/windmill-api/src/database_triggers.rs +++ b/backend/windmill-api/src/database_triggers.rs @@ -9,11 +9,40 @@ use windmill_common::{db::UserDB, error, utils::StripPath}; use crate::db::ApiAuthed; +#[derive(Serialize, Deserialize)] +struct Database { + username: String, + password: Option, + host: String, + port: u16, +} + +impl Database { + pub fn new(username: String, password: Option, host: String, port: u16) -> Self { + Self { username, password, host, port } + } +} + +#[derive(Serialize, Deserialize)] +struct TableTrack { + table_name: String, + columns_name: Vec, +} + #[derive(Deserialize)] struct EditDatabaseTrigger {} #[derive(Deserialize, Serialize)] -struct DatabaseTrigger {} +struct CreateDatabaseTrigger { + database: Database, + table_to_track: Option>, +} + +#[derive(Deserialize, Serialize)] +struct DatabaseTrigger { + database: Database, + table_to_track: Option>, +} #[derive(Deserialize, Serialize)] pub struct ListDatabaseTriggerQuery { @@ -28,6 +57,7 @@ async fn create_database_trigger( authed: ApiAuthed, Extension(user_db): Extension, Path(w_id): Path, + Json(CreateDatabaseTrigger { database, table_to_track }): Json, ) -> error::Result<(StatusCode, String)> { Ok((StatusCode::OK, "OK".to_string())) } @@ -46,7 +76,7 @@ async fn get_database_trigger( Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> error::JsonResult { - Ok(Json(DatabaseTrigger {})) + todo!() } async fn update_database_trigger( diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index 1835c4f3301c7..7388b4acf9c15 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -473,7 +473,7 @@ const selectedIdStore = writable(selectedId ?? 'settings-metadata') const selectedTriggerStore = writable< - 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' + 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'database' | 'scheduledPoll' >('webhooks') export function getSelectedId() { @@ -502,6 +502,7 @@ | 'cli' | 'routes' | 'websockets' + | 'database' | 'scheduledPoll' ) { selectedTriggerStore.set(selectedTrigger) diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 4728ae8294b34..3635adc3d1694 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -109,7 +109,7 @@ ) const simplifiedPoll = writable(false) const selectedTriggerStore = writable< - 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' + 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' | 'database' >('webhooks') export function setPrimarySchedule(schedule: ScheduleTrigger | undefined | false) { diff --git a/frontend/src/lib/components/details/DetailPageDetailPanel.svelte b/frontend/src/lib/components/details/DetailPageDetailPanel.svelte index 7d998aa21aeb8..0973b4325aa3c 100644 --- a/frontend/src/lib/components/details/DetailPageDetailPanel.svelte +++ b/frontend/src/lib/components/details/DetailPageDetailPanel.svelte @@ -12,6 +12,7 @@ | 'cli' | 'routes' | 'websockets' + | 'database' | 'scheduledPoll' = 'webhooks' export let flow_json: any | undefined = undefined export let simplfiedPoll: boolean = false @@ -51,6 +52,7 @@ + diff --git a/frontend/src/lib/components/details/DetailPageLayout.svelte b/frontend/src/lib/components/details/DetailPageLayout.svelte index 2fb070cbce1c6..853b4506c68c3 100644 --- a/frontend/src/lib/components/details/DetailPageLayout.svelte +++ b/frontend/src/lib/components/details/DetailPageLayout.svelte @@ -20,7 +20,14 @@ const primaryScheduleStore = writable(undefined) const selectedTriggerStore = writable< - 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' + | 'webhooks' + | 'emails' + | 'schedules' + | 'cli' + | 'routes' + | 'websockets' + | 'database' + | 'scheduledPoll' >('webhooks') const simplifiedPoll = writable(false) @@ -52,6 +59,7 @@ + @@ -94,6 +102,7 @@ + diff --git a/frontend/src/lib/components/details/DetailPageTriggerPanel.svelte b/frontend/src/lib/components/details/DetailPageTriggerPanel.svelte index 144cd1d865f49..5b8fbe29cdc6e 100644 --- a/frontend/src/lib/components/details/DetailPageTriggerPanel.svelte +++ b/frontend/src/lib/components/details/DetailPageTriggerPanel.svelte @@ -11,6 +11,7 @@ | 'cli' | 'routes' | 'websockets' + | 'database' | 'scheduledPoll' = 'webhooks' export let simplfiedPoll: boolean = false @@ -43,6 +44,12 @@ Websockets + + + + Database + + @@ -69,6 +76,8 @@ {:else if triggerSelected === 'websockets'} + {:else if triggerSelected === 'database'} + {:else if triggerSelected === 'cli'} {/if} diff --git a/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte b/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte index ace322ad65a3f..4fd9d8df983e5 100644 --- a/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte +++ b/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte @@ -1,5 +1,5 @@ diff --git a/frontend/src/lib/components/graph/util.ts b/frontend/src/lib/components/graph/util.ts index fb2ac009a11ce..4304962f71f77 100644 --- a/frontend/src/lib/components/graph/util.ts +++ b/frontend/src/lib/components/graph/util.ts @@ -1,7 +1,7 @@ import type { FlowStatusModule } from '$lib/gen' export const NODE = { - width: 275, + width: 285, height: 34, gap: { horizontal: 40, diff --git a/frontend/src/lib/components/triggers.ts b/frontend/src/lib/components/triggers.ts index 4c06603f20ae7..90fb1a3401eeb 100644 --- a/frontend/src/lib/components/triggers.ts +++ b/frontend/src/lib/components/triggers.ts @@ -11,7 +11,7 @@ export type ScheduleTrigger = { export type TriggerContext = { selectedTrigger: Writable< - 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' + 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' | 'database' > primarySchedule: Writable triggersCount: Writable diff --git a/frontend/src/lib/components/triggers/TriggersEditor.svelte b/frontend/src/lib/components/triggers/TriggersEditor.svelte index 5e0cebef7aff0..628a253abb56f 100644 --- a/frontend/src/lib/components/triggers/TriggersEditor.svelte +++ b/frontend/src/lib/components/triggers/TriggersEditor.svelte @@ -1,17 +1,18 @@ + +{#if open} + +{/if} diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte new file mode 100644 index 0000000000000..600cd4c1803ca --- /dev/null +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -0,0 +1,634 @@ + + + + + + {#if !drawerLoading && can_write} + {#if edit} +
+ { + await WebsocketTriggerService.setWebsocketTriggerEnabled({ + path: initialPath, + workspace: $workspaceStore ?? '', + requestBody: { enabled: e.detail } + }) + sendUserToast( + `${e.detail ? 'enabled' : 'disabled'} websocket trigger ${initialPath}` + ) + }} + /> +
+ {/if} + + {/if} +
+ {#if drawerLoading} + + {:else} + + {#if edit} + Changes can take up to 30 seconds to take effect. + {:else} + New websocket triggers can take up to 30 seconds to start listening. + {/if} + +
+
+ +
+ +
+
+ { + url = ev.detail === 'runnable' ? '$script:' : '' + url_runnable_args = {} + }} + > + + + +
+ {#if url.startsWith('$')} +
+
+
+
+ Runnable + +
+
+ { + dirtyUrl = true + const { path, itemKind } = ev.detail + url = `$${itemKind}:${path ?? ''}` + }} + /> +
+ {dirtyUrl ? urlError : ''} +
+
+
+ + {#if url.split(':')[1]?.length > 0} + {#if urlRunnableSchema} +

Arguments

+ {#await import('$lib/components/SchemaForm.svelte')} + + {:then Module} + + {/await} + {#if urlRunnableSchema.properties && Object.keys(urlRunnableSchema.properties).length === 0} +
This runnable takes no arguments
+ {/if} + {:else} + + {/if} + {/if} + {:else} +
+ +
+ {/if} +
+ +
+

+ Pick a script or flow to be triggered +

+
+ +
+
+ +
+

+ Initial messages are sent at the beginning of the connection. They are sent in order.
+ Raw messages and runnable results are supported. +

+
+ {#each initial_messages as v, i} +
+
+
+ +
+ {#if 'raw_message' in v} +
+
+ Raw JSON message (if a string, wrapping quotes will be discarded) +
+ { + const { code } = ev.detail + v = { + raw_message: code + } + }} + code={v.raw_message} + /> +
+ {:else if 'runnable_result' in v} +
+
Runnable
+ { + const { path, itemKind } = ev.detail + v = { + runnable_result: { + path: path ?? '', + args: {}, + is_flow: itemKind === 'flow' + } + } + }} + /> + + {#if v.runnable_result?.path} + {@const schema = + initialMessageRunnableSchemas[ + v.runnable_result.is_flow + ? 'flow/' + v.runnable_result.path + : v.runnable_result.path + ]} + {#if schema} +

Arguments

+ {#await import('$lib/components/SchemaForm.svelte')} + + {:then Module} + + {/await} + {#if schema && schema.properties && Object.keys(schema.properties).length === 0} +
This runnable takes no arguments
+ {/if} + {:else} + + {/if} + {/if} +
+ {:else} + Unknown type + {/if} +
+ +
+ {/each} + +
+ +
+
+
+ +
+

+ Filters will limit the execution of the trigger to only messages that match all + criteria.
+ The JSON filter checks if the value at the key is equal or a superset of the filter value. +

+
+ {#each filters as v, i} +
+
+
+ +
+ + + +
+ +
+ {/each} + +
+ +
+
+
+
+ {/if} +
+
diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte new file mode 100644 index 0000000000000..e66ca86e9d0ba --- /dev/null +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte @@ -0,0 +1,106 @@ + + + { + loadTriggers() + }} + bind:this={databaseTriggerEditor} +/> + +
+ {#if !newItem} + {#if isCloudHosted()} + + Database triggers are disabled in the multi-tenant cloud. + + {:else if $userStore?.is_admin || $userStore?.is_super_admin} + + {:else} + + {/if} + {/if} + + {#if databaseTriggers} + {#if databaseTriggers.length == 0} +
No Database triggers
+ {:else} +
+ {#each databaseTriggers as databaseTriggers (databaseTriggers.path)} +
+
{databaseTriggers.path}
+
+ {databaseTriggers.url} +
+
+ +
+
+ {/each} +
+ {/if} + {:else} + + {/if} + + {#if newItem} + + Deploy the {isFlow ? 'flow' : 'script'} to add Database triggers. + + {/if} +
diff --git a/frontend/src/lib/components/triggers/RouteEditor.svelte b/frontend/src/lib/components/triggers/http_triggers/RouteEditor.svelte similarity index 100% rename from frontend/src/lib/components/triggers/RouteEditor.svelte rename to frontend/src/lib/components/triggers/http_triggers/RouteEditor.svelte diff --git a/frontend/src/lib/components/triggers/RouteEditorInner.svelte b/frontend/src/lib/components/triggers/http_triggers/RouteEditorInner.svelte similarity index 96% rename from frontend/src/lib/components/triggers/RouteEditorInner.svelte rename to frontend/src/lib/components/triggers/http_triggers/RouteEditorInner.svelte index 65fc7db264ef9..38d41b87b7ea8 100644 --- a/frontend/src/lib/components/triggers/RouteEditorInner.svelte +++ b/frontend/src/lib/components/triggers/http_triggers/RouteEditorInner.svelte @@ -12,17 +12,17 @@ import Section from '$lib/components/Section.svelte' import { Loader2, Save, Pipette } from 'lucide-svelte' import Label from '$lib/components/Label.svelte' - import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte' - import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte' import { page } from '$app/stores' import { isCloudHosted } from '$lib/cloud' import { base } from '$lib/base' - import S3FilePicker from '../S3FilePicker.svelte' - import Toggle from '../Toggle.svelte' - import JsonEditor from '../apps/editor/settingsPanel/inputEditor/JsonEditor.svelte' - import FileUpload from '../common/fileUpload/FileUpload.svelte' - import SimpleEditor from '../SimpleEditor.svelte' - + import JsonEditor from '$lib/components/apps/editor/settingsPanel/inputEditor/JsonEditor.svelte' + import FileUpload from '$lib/components/common/fileUpload/FileUpload.svelte' + import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' + import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' + import S3FilePicker from '$lib/components/S3FilePicker.svelte' + import Toggle from '$lib/components/Toggle.svelte' + import SimpleEditor from '$lib/components/SimpleEditor.svelte' + let is_flow: boolean = false let initialPath = '' let edit = true diff --git a/frontend/src/lib/components/triggers/RoutesPanel.svelte b/frontend/src/lib/components/triggers/http_triggers/RoutesPanel.svelte similarity index 92% rename from frontend/src/lib/components/triggers/RoutesPanel.svelte rename to frontend/src/lib/components/triggers/http_triggers/RoutesPanel.svelte index 12feac5bd8e07..dbb82fdeac14a 100644 --- a/frontend/src/lib/components/triggers/RoutesPanel.svelte +++ b/frontend/src/lib/components/triggers/http_triggers/RoutesPanel.svelte @@ -1,15 +1,15 @@ - + websocketTriggerEditor.openNew(false)} + on:click={() => databaseTriggerEditor.openNew(false)} > New Database trigger @@ -271,7 +271,7 @@ websocketTriggerEditor?.openEdit(path, is_flow)} + on:click={() => databaseTriggerEditor?.openEdit(path, is_flow)} class="min-w-0 grow hover:underline decoration-gray-400" >
@@ -337,7 +337,7 @@
+ +
+ +
+
+ +
+ +
+
Date: Sun, 17 Nov 2024 20:51:23 +0100 Subject: [PATCH 05/50] feat: :construction: add definition in yaml, updated backend code and added migration --- .../20241123152203_database_triggers.down.sql | 1 + .../20241123152203_database_triggers.up.sql | 1 + backend/windmill-api/openapi.yaml | 248 ++++++++++++++++++ backend/windmill-api/src/database_triggers.rs | 22 +- frontend/openapi-ts-error-1732381784283.log | 28 ++ 5 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 backend/migrations/20241123152203_database_triggers.down.sql create mode 100644 backend/migrations/20241123152203_database_triggers.up.sql create mode 100644 frontend/openapi-ts-error-1732381784283.log diff --git a/backend/migrations/20241123152203_database_triggers.down.sql b/backend/migrations/20241123152203_database_triggers.down.sql new file mode 100644 index 0000000000000..d2f607c5b8bd6 --- /dev/null +++ b/backend/migrations/20241123152203_database_triggers.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20241123152203_database_triggers.up.sql b/backend/migrations/20241123152203_database_triggers.up.sql new file mode 100644 index 0000000000000..0da0a538a399f --- /dev/null +++ b/backend/migrations/20241123152203_database_triggers.up.sql @@ -0,0 +1 @@ +-- Add up migration script here diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 1685505324788..e9addb0522bbc 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -7685,6 +7685,149 @@ paths: schema: type: string + /w/{workspace}/database_triggers/create: + post: + summary: create database trigger + operationId: createDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new database trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewDatabaseTrigger" + responses: + "201": + description: database trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/update/{path}: + post: + summary: update database trigger + operationId: updateDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditDatabaseTrigger" + responses: + "200": + description: database trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/delete/{path}: + delete: + summary: delete database trigger + operationId: deleteDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: database trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/list: + get: + summary: list database triggers + operationId: listDatabaseTriggers + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: database trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DatabaseTrigger" + + /w/{workspace}/database_triggers/exists/{path}: + get: + summary: does database trigger exists + operationId: existsDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: database trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/database_triggers/setenabled/{path}: + post: + summary: set enabled database trigger + operationId: setDatabaseTriggerEnabled + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated database trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: database trigger enabled set + content: + text/plain: + schema: + type: string /groups/list: get: @@ -8554,6 +8697,7 @@ paths: raw_app, http_trigger, websocket_trigger, + database_trigger ] responses: "200": @@ -8592,6 +8736,7 @@ paths: raw_app, http_trigger, websocket_trigger, + database_trigger ] requestBody: description: acl to add @@ -8641,6 +8786,7 @@ paths: raw_app, http_trigger, websocket_trigger, + database_trigger ] requestBody: description: acl to add @@ -11932,6 +12078,8 @@ components: type: number websocket_count: type: number + database_count: + type: number WebsocketTrigger: type: object @@ -12103,6 +12251,106 @@ components: required: - runnable_result + TableToTrack: + type: object + properties: + table_name: + type: string + columns_name: + type: array + items: + type: string + required: + - table_name + + DatabaseToTrack: + type: object + properties: + username: string + password: string + host: string + port: number + + DatabaseTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + database: + $ref: "#/components/schemas/DatabaseToTrack" + required: + - username + - host + - port + table_to_track: + type: array + items: + $ref: "#/components/schemas/TableToTrack" + + required: + - path + - script_path + - is_flow + - enabled + - database + + NewDatabaseTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + database: + $ref: "#/components/schemas/DatabaseToTrack" + required: + - username + - host + - port + table_to_track: + type: array + items: + $ref: "#/components/schemas/TableToTrack" + + required: + - path + - script_path + - is_flow + - enabled + - database + + EditDatabaseTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + database: + $ref: "#/components/schemas/DatabaseToTrack" + required: + - username + - host + - port + table_to_track: + type: array + items: + $ref: "#/components/schemas/TableToTrack" + Group: type: object properties: diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs index 2d50f63a5f9b0..9b22b18a0b916 100644 --- a/backend/windmill-api/src/database_triggers.rs +++ b/backend/windmill-api/src/database_triggers.rs @@ -5,7 +5,7 @@ use axum::{ }; use http::StatusCode; use serde::{Deserialize, Serialize}; -use windmill_common::{db::UserDB, error, utils::StripPath}; +use windmill_common::{db::UserDB, error, utils::StripPath, worker::CLOUD_HOSTED}; use crate::db::ApiAuthed; @@ -26,14 +26,18 @@ impl Database { #[derive(Serialize, Deserialize)] struct TableTrack { table_name: String, - columns_name: Vec, + columns_name: Option>, } #[derive(Deserialize)] struct EditDatabaseTrigger {} #[derive(Deserialize, Serialize)] -struct CreateDatabaseTrigger { +struct NewDatabaseTrigger { + path: String, + script_path: String, + is_flow: bool, + enabled: bool, database: Database, table_to_track: Option>, } @@ -57,8 +61,17 @@ async fn create_database_trigger( authed: ApiAuthed, Extension(user_db): Extension, Path(w_id): Path, - Json(CreateDatabaseTrigger { database, table_to_track }): Json, + Json(new_database_trigger): Json, ) -> error::Result<(StatusCode, String)> { + let NewDatabaseTrigger { database, table_to_track, path, script_path, enabled, is_flow } = + new_database_trigger; + if *CLOUD_HOSTED { + return Err(error::Error::BadRequest( + "Database triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), + )); + } + let mut tx = user_db.begin(&authed).await?; + Ok((StatusCode::OK, "OK".to_string())) } @@ -77,6 +90,7 @@ async fn get_database_trigger( Path((w_id, path)): Path<(String, StripPath)>, ) -> error::JsonResult { todo!() + todo!() } async fn update_database_trigger( diff --git a/frontend/openapi-ts-error-1732381784283.log b/frontend/openapi-ts-error-1732381784283.log new file mode 100644 index 0000000000000..80a69e4c8faaf --- /dev/null +++ b/frontend/openapi-ts-error-1732381784283.log @@ -0,0 +1,28 @@ +Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (12261:18) + + 12258 | type: string + 12259 | columns_name: + 12260 | type: array + 12261 | items: +--------------------------^ + 12262 | type: string + 12263 | required: +JSONParserError: Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (12261:18) + + 12258 | type: string + 12259 | columns_name: + 12260 | type: array + 12261 | items: +--------------------------^ + 12262 | type: string + 12263 | required: + at Object.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23) + at getResult (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22) + at runNextPlugin (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32) + at /home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9 + at new Promise () + at Object.run (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12) + at parseFile (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38) + at parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30) + at async $RefParser.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28) + at async $RefParser.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13) \ No newline at end of file From 68c3a3c561000bdb5983f9cf94250d5c34602bf7 Mon Sep 17 00:00:00 2001 From: dieriba Date: Sun, 17 Nov 2024 20:51:23 +0100 Subject: [PATCH 06/50] feat: :construction: updated migration file, update openapi.yml, updated database_triggers page and backend function --- .../20241123152203_database_triggers.down.sql | 1 + .../20241123152203_database_triggers.up.sql | 14 + backend/openapi.yaml | 13333 ++++++++++++++++ backend/windmill-api/openapi.yaml | 116 +- backend/windmill-api/src/database_triggers.rs | 142 +- frontend/openapi-ts-error-1732447138336.log | 12 + frontend/src/lib/components/Path.svelte | 11 +- .../DatabaseTriggerEditorInner.svelte | 481 +- .../(logged)/database_triggers/+page.svelte | 52 +- 9 files changed, 13632 insertions(+), 530 deletions(-) create mode 100644 backend/openapi.yaml create mode 100644 frontend/openapi-ts-error-1732447138336.log diff --git a/backend/migrations/20241123152203_database_triggers.down.sql b/backend/migrations/20241123152203_database_triggers.down.sql index d2f607c5b8bd6..2900ae5dcccb1 100644 --- a/backend/migrations/20241123152203_database_triggers.down.sql +++ b/backend/migrations/20241123152203_database_triggers.down.sql @@ -1 +1,2 @@ -- Add down migration script here +DROP TABLE IF EXISTS database_trigger; \ No newline at end of file diff --git a/backend/migrations/20241123152203_database_triggers.up.sql b/backend/migrations/20241123152203_database_triggers.up.sql index 0da0a538a399f..c429ad5cced9f 100644 --- a/backend/migrations/20241123152203_database_triggers.up.sql +++ b/backend/migrations/20241123152203_database_triggers.up.sql @@ -1 +1,15 @@ -- Add up migration script here +CREATE TABLE database_trigger( + path VARCHAR(255) NOT NULL, + script_path VARCHAR(255) NOT NULL, + is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + edited_by VARCHAR(50) NOT NULL, + email VARCHAR(255) NOT NULL, + edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + extra_perms JSONB NOT NULL DEFAULT '{}', + database JSONB NOT NULL, + table_to_track JSONB[], + error TEXT NULL, + CONSTRAINT PK_database_trigger PRIMARY KEY (path,workspace_id) +); \ No newline at end of file diff --git a/backend/openapi.yaml b/backend/openapi.yaml new file mode 100644 index 0000000000000..91c19fc75f237 --- /dev/null +++ b/backend/openapi.yaml @@ -0,0 +1,13333 @@ +openapi: "3.0.3" + +info: + version: 1.425.1 + title: Windmill API + + contact: + name: Windmill Team + email: contact@windmill.dev + url: https://windmill.dev + + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + + x-logo: + url: https://windmill.dev/img/windmill.svg +externalDocs: + description: documentation portal + url: https://windmill.dev + +servers: + - url: /api + +security: + - bearerAuth: [] + - cookieAuth: [] + +paths: + /version: + get: + summary: get backend version + operationId: backendVersion + tags: + - settings + responses: + "200": + description: git version of backend + content: + text/plain: + schema: + type: string + + /uptodate: + get: + summary: is backend up to date + operationId: backendUptodate + tags: + - settings + responses: + "200": + description: is backend up to date + content: + text/plain: + schema: + type: string + + /ee_license: + get: + summary: get license id + operationId: getLicenseId + tags: + - settings + responses: + "200": + description: get license id (empty if not ee) + content: + text/plain: + schema: + type: string + + /openapi.yaml: + get: + summary: get openapi yaml spec + operationId: getOpenApiYaml + tags: + - settings + responses: + "200": + description: openapi yaml file content + content: + text/plain: + schema: + type: string + + /w/{workspace}/audit/get/{id}: + get: + summary: get audit log (requires admin privilege) + operationId: getAuditLog + tags: + - audit + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/PathId" + responses: + "200": + description: an audit log + content: + application/json: + schema: + $ref: "#/components/schemas/AuditLog" + + /w/{workspace}/audit/list: + get: + summary: list audit logs (requires admin privilege) + operationId: listAuditLogs + tags: + - audit + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/Before" + - $ref: "#/components/parameters/After" + - $ref: "#/components/parameters/Username" + - $ref: "#/components/parameters/Operation" + - name: operations + in: query + description: comma separated list of exact operations to include + schema: + type: string + - name: exclude_operations + in: query + description: comma separated list of operations to exclude + schema: + type: string + - $ref: "#/components/parameters/ResourceName" + - $ref: "#/components/parameters/ActionKind" + + responses: + "200": + description: a list of audit logs + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/AuditLog" + + /auth/login: + post: + security: [] + summary: login with password + operationId: login + tags: + - user + requestBody: + description: credentials + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Login" + + responses: + "200": + description: > + Successfully authenticated. The session ID is returned in a cookie + named `token` and as plaintext response. Preferred method of + authorization is through the bearer token. The cookie is only for + browser convenience. + + headers: + Set-Cookie: + schema: + type: string + example: token=abcde12345; Path=/; HttpOnly + content: + text/plain: + schema: + type: string + + /auth/logout: + post: + security: [] + summary: logout + operationId: logout + tags: + - user + + responses: + "200": + description: clear cookies and clear token (if applicable) + headers: + Set-Cookie: + schema: + type: string + content: + text/plain: + schema: + type: string + + /w/{workspace}/users/get/{username}: + get: + summary: get user (require admin privilege) + operationId: getUser + tags: + - user + - admin + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: username + in: path + required: true + schema: + type: string + responses: + "200": + description: user created + content: + application/json: + schema: + $ref: "#/components/schemas/User" + + /w/{workspace}/users/update/{username}: + post: + summary: update user (require admin privilege) + operationId: updateUser + tags: + - user + - admin + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: username + in: path + required: true + schema: + type: string + requestBody: + description: new user + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditWorkspaceUser" + responses: + "200": + description: edited user + content: + text/plain: + schema: + type: string + + /w/{workspace}/users/is_owner/{path}: + get: + summary: is owner of path + operationId: isOwnerOfPath + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: is owner + content: + application/json: + schema: + type: boolean + + /users/setpassword: + post: + summary: set password + operationId: setPassword + tags: + - user + requestBody: + description: set password + required: true + content: + application/json: + schema: + type: object + properties: + password: + type: string + required: + - password + responses: + "200": + description: password set + content: + text/plain: + schema: + type: string + + /users/set_password_of/{user}: + post: + summary: set password for a specific user (require super admin) + operationId: setPasswordForUser + tags: + - user + parameters: + - name: user + in: path + required: true + schema: + type: string + requestBody: + description: set password + required: true + content: + application/json: + schema: + type: object + properties: + password: + type: string + required: + - password + responses: + "200": + description: password set + content: + text/plain: + schema: + type: string + + /users/set_login_type/{user}: + post: + summary: set login type for a specific user (require super admin) + operationId: setLoginTypeForUser + tags: + - user + parameters: + - name: user + in: path + required: true + schema: + type: string + requestBody: + description: set login type + required: true + content: + application/json: + schema: + type: object + properties: + login_type: + type: string + required: + - login_type + responses: + "200": + description: login type set + content: + text/plain: + schema: + type: string + + + /users/create: + post: + summary: create user + operationId: createUserGlobally + tags: + - user + requestBody: + description: user info + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + password: + type: string + super_admin: + type: boolean + name: + type: string + company: + type: string + required: + - email + - password + - super_admin + responses: + "201": + description: user created + content: + text/plain: + schema: + type: string + + /users/update/{email}: + post: + summary: global update user (require super admin) + operationId: globalUserUpdate + tags: + - user + parameters: + - name: email + in: path + required: true + schema: + type: string + requestBody: + description: new user info + required: true + content: + application/json: + schema: + type: object + properties: + is_super_admin: + type: boolean + name: + type: string + responses: + "200": + description: user updated + content: + text/plain: + schema: + type: string + + /users/username_info/{email}: + get: + summary: global username info (require super admin) + operationId: globalUsernameInfo + tags: + - user + parameters: + - name: email + in: path + required: true + schema: + type: string + responses: + "200": + description: user renamed + content: + application/json: + schema: + type: object + properties: + username: + type: string + workspace_usernames: + type: array + items: + type: object + properties: + workspace_id: + type: string + username: + type: string + required: + - workspace_id + - username + required: + - username + - workspace_usernames + + /users/rename/{email}: + post: + summary: global rename user (require super admin) + operationId: globalUserRename + tags: + - user + parameters: + - name: email + in: path + required: true + schema: + type: string + requestBody: + description: new username + required: true + content: + application/json: + schema: + type: object + properties: + new_username: + type: string + required: + - new_username + responses: + "200": + description: user renamed + content: + text/plain: + schema: + type: string + + /users/delete/{email}: + delete: + summary: global delete user (require super admin) + operationId: globalUserDelete + tags: + - user + parameters: + - name: email + in: path + required: true + schema: + type: string + responses: + "200": + description: user deleted + content: + text/plain: + schema: + type: string + /users/overwrite: + post: + summary: global overwrite users (require super admin and EE) + operationId: globalUsersOverwrite + tags: + - user + requestBody: + description: List of users + required: true + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ExportedUser" + responses: + "200": + description: Success message + content: + text/plain: + schema: + type: string + + /users/export: + get: + summary: global export users (require super admin and EE) + operationId: globalUsersExport + tags: + - user + responses: + "200": + description: exported users + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ExportedUser" + + /w/{workspace}/users/delete/{username}: + delete: + summary: delete user (require admin privilege) + operationId: deleteUser + tags: + - user + - admin + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: username + in: path + required: true + schema: + type: string + responses: + "200": + description: delete user + content: + text/plain: + schema: + type: string + + /workspaces/list: + get: + summary: list all workspaces visible to me + operationId: listWorkspaces + tags: + - workspace + responses: + "200": + description: all workspaces + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Workspace" + + /workspaces/allowed_domain_auto_invite: + get: + summary: is domain allowed for auto invi + operationId: isDomainAllowed + tags: + - workspace + responses: + "200": + description: domain allowed or not + content: + application/json: + schema: + type: boolean + + /workspaces/users: + get: + summary: list all workspaces visible to me with user info + operationId: listUserWorkspaces + tags: + - workspace + responses: + "200": + description: workspace with associated username + content: + application/json: + schema: + $ref: "#/components/schemas/UserWorkspaceList" + + /workspaces/list_as_superadmin: + get: + summary: list all workspaces as super admin (require to be super admin) + operationId: listWorkspacesAsSuperAdmin + tags: + - workspace + parameters: + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: workspaces + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Workspace" + + /workspaces/create: + post: + summary: create workspace + operationId: createWorkspace + tags: + - workspace + requestBody: + description: new token + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateWorkspace" + responses: + "201": + description: token created + content: + text/plain: + schema: + type: string + + /workspaces/exists: + post: + summary: exists workspace + operationId: existsWorkspace + tags: + - workspace + requestBody: + description: id of workspace + required: true + content: + application/json: + schema: + type: object + properties: + id: + type: string + required: + - id + responses: + "200": + description: status + content: + text/plain: + schema: + type: boolean + + /workspaces/exists_username: + post: + summary: exists username + operationId: existsUsername + tags: + - workspace + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + id: + type: string + username: + type: string + required: + - id + - username + responses: + "200": + description: status + content: + text/plain: + schema: + type: boolean + + /settings/global/{key}: + get: + summary: get global settings + operationId: getGlobal + tags: + - setting + parameters: + - $ref: "#/components/parameters/Key" + responses: + "200": + description: status + content: + application/json: + schema: {} + + post: + summary: post global settings + operationId: setGlobal + tags: + - setting + parameters: + - $ref: "#/components/parameters/Key" + requestBody: + description: value set + required: true + content: + application/json: + schema: + type: object + properties: + value: {} + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /settings/local: + get: + summary: get local settings + operationId: getLocal + tags: + - setting + responses: + "200": + description: status + content: + application/json: + schema: {} + + /settings/test_smtp: + post: + summary: test smtp + operationId: testSmtp + tags: + - setting + requestBody: + description: test smtp payload + required: true + content: + application/json: + schema: + type: object + properties: + to: + type: string + smtp: + type: object + properties: + host: + type: string + username: + type: string + password: + type: string + port: + type: integer + from: + type: string + tls_implicit: + type: boolean + disable_tls: + type: boolean + required: + - host + - username + - password + - port + - from + - tls_implicit + - disable_tls + required: + - to + - smtp + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /settings/test_critical_channels: + post: + summary: test critical channels + operationId: testCriticalChannels + tags: + - setting + requestBody: + description: test critical channel payload + required: true + content: + application/json: + schema: + type: array + items: + type: object + properties: + email: + type: string + slack_channel: + type: string + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /settings/critical_alerts: + get: + summary: Get all critical alerts + operationId: getCriticalAlerts + tags: + - setting + parameters: + - in: query + name: page + schema: + type: integer + default: 1 + description: The page number to retrieve (minimum value is 1) + - in: query + name: page_size + schema: + type: integer + default: 10 + maximum: 100 + description: Number of alerts per page (maximum is 100) + - in: query + name: acknowledged + schema: + type: boolean + nullable: true + description: Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts + responses: + "200": + description: Successfully retrieved all critical alerts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + + /settings/critical_alerts/{id}/acknowledge: + post: + summary: Acknowledge a critical alert + operationId: acknowledgeCriticalAlert + tags: + - setting + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The ID of the critical alert to acknowledge + responses: + "200": + description: Successfully acknowledged the critical alert + content: + application/json: + schema: + type: string + example: "Critical alert acknowledged" + + /settings/critical_alerts/acknowledge_all: + post: + summary: Acknowledge all unacknowledged critical alerts + operationId: acknowledgeAllCriticalAlerts + tags: + - setting + responses: + "200": + description: Successfully acknowledged all unacknowledged critical alerts. + content: + application/json: + schema: + type: string + example: "All unacknowledged critical alerts acknowledged" + + /settings/test_license_key: + post: + summary: test license key + operationId: testLicenseKey + tags: + - setting + requestBody: + description: test license key + required: true + content: + application/json: + schema: + type: object + properties: + license_key: + type: string + required: + - license_key + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + # pub use_ssl: Option, + # #[serde(rename = "accountName")] + # pub account_name: String, + # #[serde(rename = "tenantId")] + # pub tenant_id: Option, + # #[serde(rename = "clientId")] + # pub client_id: Option, + # #[serde(rename = "containerName")] + # pub container_name: String, + # #[serde(rename = "accessKey")] + # pub access_key: Option, + + /settings/test_object_storage_config: + post: + summary: test object storage config + operationId: testObjectStorageConfig + tags: + - setting + requestBody: + description: test object storage config + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /settings/send_stats: + post: + summary: send stats + operationId: sendStats + tags: + - setting + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + + /settings/latest_key_renewal_attempt: + get: + summary: get latest key renewal attempt + operationId: getLatestKeyRenewalAttempt + tags: + - setting + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + result: + type: string + attempted_at: + type: string + format: date-time + required: + - result + - attempted_at + nullable: true + + /settings/renew_license_key: + post: + summary: renew license key + operationId: renewLicenseKey + tags: + - setting + parameters: + - name: license_key + in: query + required: false + schema: + type: string + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /settings/customer_portal: + post: + summary: create customer portal session + operationId: createCustomerPortalSession + tags: + - setting + parameters: + - name: license_key + in: query + required: false + schema: + type: string + responses: + "200": + description: url to portal + content: + text/plain: + schema: + type: string + + /saml/test_metadata: + post: + summary: test metadata + operationId: testMetadata + tags: + - setting + requestBody: + description: test metadata + required: true + content: + application/json: + schema: + type: string + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + + /settings/list_global: + get: + summary: list global settings + operationId: listGlobalSettings + tags: + - setting + responses: + "200": + description: list of settings + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/GlobalSetting" + + /users/email: + get: + summary: get current user email (if logged in) + operationId: getCurrentEmail + tags: + - user + responses: + "200": + description: user email + content: + text/plain: + schema: + type: string + + /users/refresh_token: + get: + summary: refresh the current token + operationId: refreshUserToken + tags: + - user + responses: + "200": + description: free usage + content: + text/plain: + schema: + type: string + + /users/tutorial_progress: + get: + summary: get tutorial progress + operationId: getTutorialProgress + tags: + - user + responses: + "200": + description: tutorial progress + content: + application/json: + schema: + type: object + properties: + progress: + type: integer + post: + summary: update tutorial progress + operationId: updateTutorialProgress + tags: + - user + requestBody: + description: progress update + required: true + content: + application/json: + schema: + type: object + properties: + progress: + type: integer + responses: + "200": + description: tutorial progress + content: + text/plain: + schema: + type: string + + /users/leave_instance: + post: + summary: leave instance + operationId: leaveInstance + tags: + - user + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /users/usage: + get: + summary: get current usage outside of premium workspaces + operationId: getUsage + tags: + - user + responses: + "200": + description: free usage + content: + text/plain: + schema: + type: number + + /users/all_runnables: + get: + summary: get all runnables in every workspace + operationId: getRunnable + tags: + - user + responses: + "200": + description: free all runnables + content: + application/json: + schema: + type: object + properties: + workspace: + type: string + endpoint_async: + type: string + endpoint_sync: + type: string + endpoint_openai_sync: + type: string + summary: + type: string + description: + type: string + kind: + type: string + required: + - workspace + - endpoint_async + - endpoint_sync + - endpoint_openai_sync + - summary + - kind + + /users/whoami: + get: + summary: get current global whoami (if logged in) + operationId: globalWhoami + tags: + - user + responses: + "200": + description: user email + content: + application/json: + schema: + $ref: "#/components/schemas/GlobalUserInfo" + + /users/list_invites: + get: + summary: list all workspace invites + operationId: listWorkspaceInvites + tags: + - user + responses: + "200": + description: list all workspace invites + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/WorkspaceInvite" + + /w/{workspace}/users/whoami: + get: + summary: whoami + operationId: whoami + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: user + content: + application/json: + schema: + $ref: "#/components/schemas/User" + + /users/accept_invite: + post: + summary: accept invite to workspace + operationId: acceptInvite + tags: + - user + requestBody: + description: accept invite + required: true + content: + application/json: + schema: + type: object + properties: + workspace_id: + type: string + username: + type: string + required: + - workspace_id + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /users/decline_invite: + post: + summary: decline invite to workspace + operationId: declineInvite + tags: + - user + requestBody: + description: decline invite + required: true + content: + application/json: + schema: + type: object + properties: + workspace_id: + type: string + required: + - workspace_id + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/invite_user: + post: + summary: invite user to workspace + operationId: inviteUser + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceInvite + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + is_admin: + type: boolean + operator: + type: boolean + required: + - email + - is_admin + - operator + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/add_user: + post: + summary: add user to workspace + operationId: addUser + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceInvite + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + is_admin: + type: boolean + username: + type: string + operator: + type: boolean + required: + - email + - is_admin + - operator + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/delete_invite: + post: + summary: delete user invite + operationId: delete invite + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceInvite + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + is_admin: + type: boolean + operator: + type: boolean + required: + - email + - is_admin + - operator + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/archive: + post: + summary: archive workspace + operationId: archiveWorkspace + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /workspaces/unarchive/{workspace}: + post: + summary: unarchive workspace + operationId: unarchiveWorkspace + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /workspaces/delete/{workspace}: + delete: + summary: delete workspace (require super admin) + operationId: deleteWorkspace + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/leave: + post: + summary: leave workspace + operationId: leaveWorkspace + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/get_workspace_name: + get: + summary: get workspace name + operationId: getWorkspaceName + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/change_workspace_name: + post: + summary: change workspace name + operationId: changeWorkspaceName + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + new_name: + type: string + required: + - username + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/change_workspace_id: + post: + summary: change workspace id + operationId: changeWorkspaceId + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + new_id: + type: string + new_name: + type: string + required: + - username + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/users/whois/{username}: + get: + summary: whois + operationId: whois + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: username + in: path + required: true + schema: + type: string + responses: + "200": + description: user + content: + application/json: + schema: + $ref: "#/components/schemas/User" + + /users/exists/{email}: + get: + summary: exists email + operationId: existsEmail + tags: + - user + parameters: + - name: email + in: path + required: true + schema: + type: string + responses: + "200": + description: user + content: + application/json: + schema: + type: boolean + + /users/list_as_super_admin: + get: + summary: list all users as super admin (require to be super amdin) + operationId: listUsersAsSuperAdmin + tags: + - user + parameters: + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: active_only + in: query + description: filter only active users + schema: + type: boolean + responses: + "200": + description: user + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/GlobalUserInfo" + + /w/{workspace}/workspaces/list_pending_invites: + get: + summary: list pending invites for a workspace + operationId: listPendingInvites + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: user + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/WorkspaceInvite" + + /w/{workspace}/workspaces/get_settings: + get: + summary: get settings + operationId: getSettings + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + workspace_id: + type: string + slack_name: + type: string + slack_team_id: + type: string + slack_command_script: + type: string + auto_invite_domain: + type: string + auto_invite_operator: + type: boolean + auto_add: + type: boolean + plan: + type: string + automatic_billing: + type: boolean + customer_id: + type: string + webhook: + type: string + deploy_to: + type: string + ai_resource: + $ref: "#/components/schemas/AiResource" + code_completion_enabled: + type: boolean + error_handler: + type: string + error_handler_extra_args: + $ref: "#/components/schemas/ScriptArgs" + error_handler_muted_on_cancel: + type: boolean + large_file_storage: + $ref: "#/components/schemas/LargeFileStorage" + git_sync: + $ref: "#/components/schemas/WorkspaceGitSyncSettings" + deploy_ui: + $ref: "#/components/schemas/WorkspaceDeployUISettings" + default_app: + type: string + default_scripts: + $ref: "#/components/schemas/WorkspaceDefaultScripts" + mute_critical_alerts: + type: boolean + required: + - code_completion_enabled + - automatic_billing + - error_handler_muted_on_cancel + + /w/{workspace}/workspaces/get_deploy_to: + get: + summary: get deploy to + operationId: getDeployTo + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + deploy_to: + type: string + + /w/{workspace}/workspaces/is_premium: + get: + summary: get if workspace is premium + operationId: getIsPremium + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + responses: + "200": + description: status + content: + application/json: + schema: + type: boolean + + /w/{workspace}/workspaces/premium_info: + get: + summary: get premium info + operationId: getPremiumInfo + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + premium: + type: boolean + usage: + type: number + seats: + type: number + automatic_billing: + type: boolean + required: + - premium + - automatic_billing + + /w/{workspace}/workspaces/set_automatic_billing: + post: + summary: set automatic billing + operationId: setAutomaticBilling + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: automatic billing + required: true + content: + application/json: + schema: + type: object + properties: + automatic_billing: + type: boolean + seats: + type: number + required: + - automatic_billing + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/edit_slack_command: + post: + summary: edit slack command + operationId: editSlackCommand + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceInvite + required: true + content: + application/json: + schema: + type: object + properties: + slack_command_script: + type: string + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/run_slack_message_test_job: + post: + summary: run a job that sends a message to Slack + operationId: runSlackMessageTestJob + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: path to hub script to run and its corresponding args + required: true + content: + application/json: + schema: + type: object + properties: + hub_script_path: + type: string + channel: + type: string + test_msg: + type: string + + responses: + "200": + description: status + content: + text/json: + schema: + type: object + properties: + job_uuid: + type: string + + /w/{workspace}/workspaces/edit_deploy_to: + post: + summary: edit deploy to + operationId: editDeployTo + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + deploy_to: + type: string + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/edit_auto_invite: + post: + summary: edit auto invite + operationId: editAutoInvite + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceInvite + required: true + content: + application/json: + schema: + type: object + properties: + operator: + type: boolean + invite_all: + type: boolean + auto_add: + type: boolean + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/edit_webhook: + post: + summary: edit webhook + operationId: editWebhook + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceWebhook + required: true + content: + application/json: + schema: + type: object + properties: + webhook: + type: string + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/edit_copilot_config: + post: + summary: edit copilot config + operationId: editCopilotConfig + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceCopilotConfig + required: true + content: + application/json: + schema: + type: object + required: + - code_completion_enabled + properties: + ai_resource: + $ref: "#/components/schemas/AiResource" + code_completion_enabled: + type: boolean + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/get_copilot_info: + get: + summary: get copilot info + operationId: getCopilotInfo + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + responses: + "200": + description: status + content: + text/plain: + schema: + type: object + properties: + ai_provider: + type: string + exists_ai_resource: + type: boolean + code_completion_enabled: + type: boolean + required: + - ai_provider + - exists_ai_resource + - code_completion_enabled + + /w/{workspace}/workspaces/edit_error_handler: + post: + summary: edit error handler + operationId: editErrorHandler + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceErrorHandler + required: true + content: + application/json: + schema: + type: object + properties: + error_handler: + type: string + error_handler_extra_args: + $ref: "#/components/schemas/ScriptArgs" + error_handler_muted_on_cancel: + type: boolean + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/edit_large_file_storage_config: + post: + summary: edit large file storage settings + operationId: editLargeFileStorageConfig + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: LargeFileStorage info + required: true + content: + application/json: + schema: + type: object + properties: + large_file_storage: + $ref: "#/components/schemas/LargeFileStorage" + + responses: + "200": + description: status + content: + application/json: + schema: {} + + /w/{workspace}/workspaces/edit_git_sync_config: + post: + summary: edit workspace git sync settings + operationId: editWorkspaceGitSyncConfig + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Workspace Git sync settings + required: true + content: + application/json: + schema: + type: object + properties: + git_sync_settings: + $ref: "#/components/schemas/WorkspaceGitSyncSettings" + + responses: + "200": + description: status + content: + application/json: + schema: {} + + /w/{workspace}/workspaces/edit_deploy_ui_config: + post: + summary: edit workspace deploy ui settings + operationId: editWorkspaceDeployUISettings + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Workspace deploy UI settings + required: true + content: + application/json: + schema: + type: object + properties: + deploy_ui_settings: + $ref: "#/components/schemas/WorkspaceDeployUISettings" + + responses: + "200": + description: status + content: + application/json: + schema: {} + + /w/{workspace}/workspaces/edit_default_app: + post: + summary: edit default app for workspace + operationId: editWorkspaceDefaultApp + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Workspace default app + required: true + content: + application/json: + schema: + type: object + properties: + default_app_path: + type: string + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/default_scripts: + post: + summary: edit default scripts for workspace + operationId: editDefaultScripts + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Workspace default app + content: + application/json: + schema: + $ref: "#/components/schemas/WorkspaceDefaultScripts" + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + get: + summary: get default scripts for workspace + operationId: get default scripts + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + application/json: + schema: + $ref: "#/components/schemas/WorkspaceDefaultScripts" + + /w/{workspace}/workspaces/set_environment_variable: + post: + summary: set environment variable + operationId: setEnvironmentVariable + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Workspace default app + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + value: + type: string + required: [name] + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/encryption_key: + get: + summary: retrieves the encryption key for this workspace + operationId: getWorkspaceEncryptionKey + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + key: + type: string + required: + - key + post: + summary: update the encryption key for this workspace + operationId: setWorkspaceEncryptionKey + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: New encryption key + required: true + content: + application/json: + schema: + type: object + properties: + new_key: + type: string + skip_reencrypt: + type: boolean + required: + - new_key + + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + + /w/{workspace}/workspaces/default_app: + get: + summary: get default app for workspace + operationId: getWorkspaceDefaultApp + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + default_app_path: + type: string + + /w/{workspace}/workspaces/get_large_file_storage_config: + get: + summary: get large file storage config + operationId: getLargeFileStorageConfig + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + responses: + "200": + description: status + content: + application/json: + schema: + $ref: "#/components/schemas/LargeFileStorage" + + /w/{workspace}/workspaces/usage: + get: + summary: get usage + operationId: getWorkspaceUsage + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: usage + content: + text/plain: + schema: + type: number + + /w/{workspace}/workspaces/used_triggers: + get: + summary: get used triggers + operationId: getUsedTriggers + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: status + content: + application/json: + schema: + type: object + properties: + http_routes_used: + type: boolean + websocket_used: + type: boolean + required: + - http_routes_used + - websocket_used + + /w/{workspace}/users/list: + get: + summary: list users + operationId: listUsers + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: user + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + + /w/{workspace}/users/list_usage: + get: + summary: list users usage + operationId: listUsersUsage + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: user + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UserUsage" + + /w/{workspace}/users/list_usernames: + get: + summary: list usernames + operationId: listUsernames + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: user + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/users/username_to_email/{username}: + get: + summary: get email from username + operationId: usernameToEmail + tags: + - user + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: username + in: path + required: true + schema: + type: string + responses: + "200": + description: email + content: + text/plain: + schema: + type: string + + /users/tokens/create: + post: + summary: create token + operationId: createToken + tags: + - user + requestBody: + description: new token + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewToken" + responses: + "201": + description: token created + content: + text/plain: + schema: + type: string + + /users/tokens/impersonate: + post: + summary: create token to impersonate a user (require superadmin) + operationId: createTokenImpersonate + tags: + - user + requestBody: + description: new token + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewTokenImpersonate" + responses: + "201": + description: token created + content: + text/plain: + schema: + type: string + + /users/tokens/delete/{token_prefix}: + delete: + summary: delete token + operationId: deleteToken + tags: + - user + parameters: + - name: token_prefix + in: path + required: true + schema: + type: string + responses: + "200": + description: delete token + content: + text/plain: + schema: + type: string + + /users/tokens/list: + get: + summary: list token + operationId: listTokens + tags: + - user + parameters: + - name: exclude_ephemeral + in: query + schema: + type: boolean + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: truncated token + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TruncatedToken" + + /w/{workspace}/oidc/token/{audience}: + post: + summary: get OIDC token (ee only) + operationId: getOidcToken + tags: + - oidc + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: audience + in: path + required: true + schema: + type: string + + responses: + "200": + description: new oidc token + content: + text/plain: + schema: + type: string + + /w/{workspace}/variables/create: + post: + summary: create variable + operationId: createVariable + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: already_encrypted + in: query + schema: + type: boolean + requestBody: + description: new variable + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateVariable" + responses: + "201": + description: variable created + content: + text/plain: + schema: + type: string + + /w/{workspace}/variables/encrypt: + post: + summary: encrypt value + operationId: encryptValue + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new variable + required: true + content: + application/json: + schema: + type: string + responses: + "200": + description: encrypted value + content: + text/plain: + schema: + type: string + + /w/{workspace}/variables/delete/{path}: + delete: + summary: delete variable + operationId: deleteVariable + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: variable deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/variables/update/{path}: + post: + summary: update variable + operationId: updateVariable + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: already_encrypted + in: query + schema: + type: boolean + requestBody: + description: updated variable + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditVariable" + responses: + "200": + description: variable updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/variables/get/{path}: + get: + summary: get variable + operationId: getVariable + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: decrypt_secret + description: | + ask to decrypt secret if this variable is secret + (if not secret no effect, default: true) + in: query + schema: + type: boolean + - name: include_encrypted + description: | + ask to include the encrypted value if secret and decrypt secret is not true (default: false) + in: query + schema: + type: boolean + responses: + "200": + description: variable + content: + application/json: + schema: + $ref: "#/components/schemas/ListableVariable" + + /w/{workspace}/variables/get_value/{path}: + get: + summary: get variable value + operationId: getVariableValue + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: variable + content: + application/json: + schema: + type: string + + /w/{workspace}/variables/exists/{path}: + get: + summary: does variable exists at path + operationId: existsVariable + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: variable + content: + application/json: + schema: + type: boolean + + /w/{workspace}/variables/list: + get: + summary: list variables + operationId: listVariable + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: path_start + in: query + schema: + type: string + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: variable list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ListableVariable" + + /w/{workspace}/variables/list_contextual: + get: + summary: list contextual variables + operationId: listContextualVariables + tags: + - variable + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: contextual variable list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ContextualVariable" + + /w/{workspace}/workspaces/critical_alerts: + get: + summary: Get all critical alerts for this workspace + operationId: workspaceGetCriticalAlerts + tags: + - setting + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - in: query + name: page + schema: + type: integer + default: 1 + description: The page number to retrieve (minimum value is 1) + - in: query + name: page_size + schema: + type: integer + default: 10 + maximum: 100 + description: Number of alerts per page (maximum is 100) + - in: query + name: acknowledged + schema: + type: boolean + nullable: true + description: Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts + responses: + "200": + description: Successfully retrieved all critical alerts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + + /w/{workspace}/workspaces/critical_alerts/{id}/acknowledge: + post: + summary: Acknowledge a critical alert for this workspace + operationId: workspaceAcknowledgeCriticalAlert + tags: + - setting + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - in: path + name: id + required: true + schema: + type: integer + description: The ID of the critical alert to acknowledge + responses: + "200": + description: Successfully acknowledged the critical alert + content: + application/json: + schema: + type: string + example: "Critical alert acknowledged" + + /w/{workspace}/workspaces/critical_alerts/acknowledge_all: + post: + summary: Acknowledge all unacknowledged critical alerts for this workspace + operationId: workspaceAcknowledgeAllCriticalAlerts + tags: + - setting + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: Successfully acknowledged all unacknowledged critical alerts. + content: + application/json: + schema: + type: string + example: "All unacknowledged critical alerts acknowledged" + + /w/{workspace}/workspaces/critical_alerts/mute: + post: + summary: Mute critical alert UI for this workspace + operationId: workspaceMuteCriticalAlertsUI + tags: + - setting + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Boolean flag to mute critical alerts. + required: true + content: + application/json: + schema: + type: object + properties: + mute_critical_alerts: + type: boolean + description: Whether critical alerts should be muted. + example: true + responses: + '200': + description: Successfully updated mute critical alert settings. + content: + application/json: + schema: + type: string + example: "Updated mute critical alert UI settings for workspace: workspace_id" + + /oauth/login_callback/{client_name}: + post: + security: [] + summary: login with oauth authorization flow + operationId: loginWithOauth + tags: + - user + parameters: + - $ref: "#/components/parameters/ClientName" + requestBody: + description: Partially filled script + required: true + content: + application/json: + schema: + type: object + properties: + code: + type: string + state: + type: string + + responses: + "200": + description: > + Successfully authenticated. The session ID is returned in a cookie + named `token` and as plaintext response. Preferred method of + authorization is through the bearer token. The cookie is only for + browser convenience. + + headers: + Set-Cookie: + schema: + type: string + example: token=abcde12345; Path=/; HttpOnly + content: + text/plain: + schema: + type: string + + /w/{workspace}/oauth/connect_slack_callback: + post: + summary: connect slack callback + operationId: connectSlackCallback + tags: + - oauth + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: code endpoint + required: true + content: + application/json: + schema: + type: object + properties: + code: + type: string + state: + type: string + required: + - code + - state + responses: + "200": + description: slack token + content: + text/plain: + schema: + type: string + + + /oauth/connect_slack_callback: + post: + summary: connect slack callback instance + operationId: connectSlackCallbackInstance + tags: + - oauth + requestBody: + description: code endpoint + required: true + content: + application/json: + schema: + type: object + properties: + code: + type: string + state: + type: string + required: + - code + - state + responses: + "200": + description: success message + content: + text/plain: + schema: + type: string + + /oauth/connect_callback/{client_name}: + post: + summary: connect callback + operationId: connectCallback + tags: + - oauth + parameters: + - $ref: "#/components/parameters/ClientName" + requestBody: + description: code endpoint + required: true + content: + application/json: + schema: + type: object + properties: + code: + type: string + state: + type: string + required: + - code + - state + responses: + "200": + description: oauth token + content: + application/json: + schema: + $ref: "#/components/schemas/TokenResponse" + + /w/{workspace}/oauth/create_account: + post: + summary: create OAuth account + operationId: createAccount + tags: + - oauth + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: code endpoint + required: true + content: + application/json: + schema: + type: object + properties: + refresh_token: + type: string + expires_in: + type: integer + client: + type: string + required: + - expires_in + - client + responses: + "200": + description: account set + content: + text/plain: + schema: + type: string + + /w/{workspace}/oauth/refresh_token/{id}: + post: + summary: refresh token + operationId: refreshToken + tags: + - oauth + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/AccountId" + requestBody: + description: variable path + required: true + content: + application/json: + schema: + type: object + properties: + path: + type: string + required: + - path + responses: + "200": + description: token refreshed + content: + text/plain: + schema: + type: string + + /w/{workspace}/oauth/disconnect/{id}: + post: + summary: disconnect account + operationId: disconnectAccount + tags: + - oauth + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/AccountId" + responses: + "200": + description: disconnected client + content: + text/plain: + schema: + type: string + + /w/{workspace}/oauth/disconnect_slack: + post: + summary: disconnect slack + operationId: disconnectSlack + tags: + - oauth + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: disconnected slack + content: + text/plain: + schema: + type: string + + /oauth/list_logins: + get: + summary: list oauth logins + operationId: listOAuthLogins + tags: + - oauth + responses: + "200": + description: list of oauth and saml login clients + content: + application/json: + schema: + type: object + properties: + oauth: + type: array + items: + type: object + properties: + type: + type: string + display_name: + type: string + required: + - type + saml: + type: string + required: + - oauth + + /oauth/list_connects: + get: + summary: list oauth connects + operationId: listOAuthConnects + tags: + - oauth + responses: + "200": + description: list of oauth connects clients + content: + application/json: + schema: + type: array + items: + type: string + + /oauth/get_connect/{client}: + get: + summary: get oauth connect + operationId: getOAuthConnect + tags: + - oauth + parameters: + - name: client + description: client name + in: path + required: true + schema: + type: string + responses: + "200": + description: get + content: + application/json: + schema: + type: object + properties: + extra_params: + type: object + scopes: + type: array + items: + type: string + + + + + /w/{workspace}/resources/create: + post: + summary: create resource + operationId: createResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: update_if_exists + in: query + schema: + type: boolean + requestBody: + description: new resource + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateResource" + responses: + "201": + description: resource created + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/delete/{path}: + delete: + summary: delete resource + operationId: deleteResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: resource deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/update/{path}: + post: + summary: update resource + operationId: updateResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated resource + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditResource" + responses: + "200": + description: resource updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/update_value/{path}: + post: + summary: update resource value + operationId: updateResourceValue + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated resource + required: true + content: + application/json: + schema: + type: object + properties: + value: {} + responses: + "200": + description: resource value updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/get/{path}: + get: + summary: get resource + operationId: getResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: resource + content: + application/json: + schema: + $ref: "#/components/schemas/Resource" + + /w/{workspace}/resources/get_value_interpolated/{path}: + get: + summary: get resource interpolated (variables and resources are fully unrolled) + operationId: getResourceValueInterpolated + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: job_id + description: job id + in: query + schema: + type: string + format: uuid + responses: + "200": + description: resource value + content: + application/json: + schema: {} + + /w/{workspace}/resources/get_value/{path}: + get: + summary: get resource value + operationId: getResourceValue + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: resource value + content: + application/json: + schema: {} + + /w/{workspace}/resources/exists/{path}: + get: + summary: does resource exists + operationId: existsResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: does resource exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/resources/list: + get: + summary: list resources + operationId: listResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: resource_type + description: resource_types to list from, separated by ',', + in: query + schema: + type: string + - name: resource_type_exclude + description: resource_types to not list from, separated by ',', + in: query + schema: + type: string + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: resource list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ListableResource" + + /w/{workspace}/resources/list_search: + get: + summary: list resources for search + operationId: listSearchResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: resource list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + value: {} + required: + - path + - value + + /w/{workspace}/resources/list_names/{name}: + get: + summary: list resource names + operationId: listResourceNames + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: resource list names + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + path: + type: string + required: + - name + - path + + /w/{workspace}/resources/type/create: + post: + summary: create resource_type + operationId: createResourceType + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new resource_type + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ResourceType" + responses: + "201": + description: resource_type created + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/file_resource_type_to_file_ext_map: + get: + summary: get map from resource type to format extension + operationId: fileResourceTypeToFileExtMap + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: map from resource type to file ext + content: + application/json: + schema: {} + + + /w/{workspace}/resources/type/delete/{path}: + delete: + summary: delete resource_type + operationId: deleteResourceType + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: resource_type deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/type/update/{path}: + post: + summary: update resource_type + operationId: updateResourceType + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated resource_type + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditResourceType" + responses: + "200": + description: resource_type updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/resources/type/get/{path}: + get: + summary: get resource_type + operationId: getResourceType + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: resource_type deleted + content: + application/json: + schema: + $ref: "#/components/schemas/ResourceType" + + /w/{workspace}/resources/type/exists/{path}: + get: + summary: does resource_type exists + operationId: existsResourceType + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: does resource_type exist + content: + application/json: + schema: + type: boolean + + /w/{workspace}/resources/type/list: + get: + summary: list resource_types + operationId: listResourceType + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: resource_type list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ResourceType" + + /w/{workspace}/resources/type/listnames: + get: + summary: list resource_types names + operationId: listResourceTypeNames + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: resource_type list + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/embeddings/query_resource_types: + get: + summary: query resource types by similarity + operationId: queryResourceTypes + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: text + description: query text + in: query + required: true + schema: + type: string + - name: limit + description: query limit + in: query + required: false + schema: + type: number + responses: + "200": + description: resource type details + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + score: + type: number + schema: {} + required: + - name + - score + + /integrations/hub/list: + get: + summary: list hub integrations + operationId: listHubIntegrations + tags: + - integration + parameters: + - name: kind + description: query integrations kind + in: query + required: false + schema: + type: string + responses: + "200": + description: integrations details + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + required: + - name + + /flows/hub/list: + get: + summary: list all hub flows + operationId: listHubFlows + tags: + - flow + responses: + "200": + description: hub flows list + content: + application/json: + schema: + type: object + properties: + flows: + type: array + items: + type: object + properties: + id: + type: number + flow_id: + type: number + summary: + type: string + apps: + type: array + items: + type: string + approved: + type: boolean + votes: + type: number + + required: + - id + - flow_id + - summary + - apps + - approved + - votes + + /flows/hub/get/{id}: + get: + summary: get hub flow by id + operationId: getHubFlowById + tags: + - flow + parameters: + - $ref: "#/components/parameters/PathId" + responses: + "200": + description: flow + content: + application/json: + schema: + type: object + properties: + flow: + $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" + + /apps/hub/list: + get: + summary: list all hub apps + operationId: listHubApps + tags: + - app + responses: + "200": + description: hub apps list + content: + application/json: + schema: + type: object + properties: + apps: + type: array + items: + type: object + properties: + id: + type: number + app_id: + type: number + summary: + type: string + apps: + type: array + items: + type: string + approved: + type: boolean + votes: + type: number + required: + - id + - app_id + - summary + - apps + - approved + - votes + + /apps/hub/get/{id}: + get: + summary: get hub app by id + operationId: getHubAppById + tags: + - app + parameters: + - $ref: "#/components/parameters/PathId" + responses: + "200": + description: app + content: + application/json: + schema: + type: object + properties: + app: + type: object + properties: + summary: + type: string + value: {} + required: + - summary + - value + required: + - app + + /scripts/hub/get/{path}: + get: + summary: get hub script content by path + operationId: getHubScriptContentByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script details + content: + text/plain: + schema: + type: string + + /scripts/hub/get_full/{path}: + get: + summary: get full hub script by path + operationId: getHubScriptByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script details + content: + application/json: + schema: + type: object + properties: + content: + type: string + lockfile: + type: string + schema: {} + language: + type: string + summary: + type: string + required: + - content + - language + + /scripts/hub/top: + get: + summary: get top hub scripts + operationId: getTopHubScripts + tags: + - script + parameters: + - name: limit + description: query limit + in: query + required: false + schema: + type: number + - name: app + description: query scripts app + in: query + required: false + schema: + type: string + - name: kind + description: query scripts kind + in: query + required: false + schema: + type: string + responses: + "200": + description: hub scripts list + content: + application/json: + schema: + type: object + properties: + asks: + type: array + items: + type: object + properties: + id: + type: number + ask_id: + type: number + summary: + type: string + app: + type: string + version_id: + type: number + kind: + $ref: "#/components/schemas/HubScriptKind" + votes: + type: number + views: + type: number + required: + - id + - ask_id + - summary + - app + - version_id + - kind + - views + - votes + + /embeddings/query_hub_scripts: + get: + summary: query hub scripts by similarity + operationId: queryHubScripts + tags: + - script + parameters: + - name: text + description: query text + in: query + required: true + schema: + type: string + - name: kind + description: query scripts kind + in: query + required: false + schema: + type: string + - name: limit + description: query limit + in: query + required: false + schema: + type: number + - name: app + description: query scripts app + in: query + required: false + schema: + type: string + responses: + "200": + description: script details + content: + application/json: + schema: + type: array + items: + type: object + properties: + ask_id: + type: number + id: + type: number + version_id: + type: number + summary: + type: string + app: + type: string + kind: + $ref: "#/components/schemas/HubScriptKind" + score: + type: number + required: + - ask_id + - id + - version_id + - summary + - app + - kind + - score + + /w/{workspace}/scripts/list_search: + get: + summary: list scripts for search + operationId: listSearchScript + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: script list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + content: + type: string + required: + - path + - content + + /w/{workspace}/scripts/list: + get: + summary: list all scripts + operationId: listScripts + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - name: path_start + description: mask to filter matching starting path + in: query + schema: + type: string + - name: path_exact + description: mask to filter exact matching path + in: query + schema: + type: string + - name: first_parent_hash + description: mask to filter scripts whom first direct parent has exact hash + in: query + schema: + type: string + - name: last_parent_hash + description: | + mask to filter scripts whom last parent in the chain has exact hash. + Beware that each script stores only a limited number of parents. Hence + the last parent hash for a script is not necessarily its top-most parent. + To find the top-most parent you will have to jump from last to last hash + until finding the parent + in: query + schema: + type: string + - name: parent_hash + description: | + is the hash present in the array of stored parent hashes for this script. + The same warning applies than for last_parent_hash. A script only store a + limited number of direct parent + in: query + schema: + type: string + - name: show_archived + description: | + (default false) + show only the archived files. + when multiple archived hash share the same path, only the ones with the latest create_at + are + ed. + in: query + schema: + type: boolean + - name: include_without_main + description: | + (default false) + include scripts without an exported main function + in: query + schema: + type: boolean + - name: include_draft_only + description: | + (default false) + include scripts that have no deployed version + in: query + schema: + type: boolean + - name: is_template + description: | + (default regardless) + if true show only the templates + if false show only the non templates + if not defined, show all regardless of if the script is a template + in: query + schema: + type: boolean + - name: kinds + description: | + (default regardless) + script kinds to filter, split by comma + in: query + schema: + type: string + - name: starred_only + description: | + (default false) + show only the starred items + in: query + schema: + type: boolean + - name: with_deployment_msg + description: | + (default false) + include deployment message + in: query + schema: + type: boolean + responses: + "200": + description: All scripts + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Script" + + /w/{workspace}/scripts/list_paths: + get: + summary: list all scripts paths + operationId: listScriptPaths + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: list of script paths + content: + text/plain: + schema: + type: array + items: + type: string + + /w/{workspace}/drafts/create: + post: + summary: create draft + operationId: createDraft + tags: + - draft + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + path: + type: string + typ: + type: string + enum: ["flow", "script", "app"] + value: {} + required: + - path + - typ + - enum + responses: + "201": + description: draft created + content: + text/plain: + schema: + type: string + + /w/{workspace}/drafts/delete/{kind}/{path}: + delete: + summary: delete draft + operationId: deleteDraft + tags: + - draft + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: kind + in: path + required: true + schema: + type: string + enum: + - script + - flow + - app + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: draft deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/scripts/create: + post: + summary: create script + operationId: createScript + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Partially filled script + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewScript" + + responses: + "201": + description: script created + content: + text/plain: + schema: + type: string + + /w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}: + post: + summary: Toggle ON and OFF the workspace error handler for a given script + operationId: toggleWorkspaceErrorHandlerForScript + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: Workspace error handler enabled + required: true + content: + application/json: + schema: + type: object + properties: + muted: + type: boolean + responses: + "200": + description: error handler toggled + content: + text/plain: + schema: + type: string + + /workers/custom_tags: + get: + summary: get all instance custom tags (tags are used to dispatch jobs to + different worker groups) + operationId: getCustomTags + tags: + - worker + responses: + "200": + description: list of custom tags + content: + application/json: + schema: + type: array + items: + type: string + + /workers/get_default_tags: + get: + summary: get all instance default tags + operationId: geDefaultTags + tags: + - worker + responses: + "200": + description: list of default tags + content: + application/json: + schema: + type: array + items: + type: string + + /workers/is_default_tags_per_workspace: + get: + summary: is default tags per workspace + operationId: isDefaultTagsPerWorkspace + tags: + - worker + responses: + "200": + description: is the default tags per workspace + content: + application/json: + schema: + type: boolean + + /w/{workspace}/scripts/archive/p/{path}: + post: + summary: archive script by path + operationId: archiveScriptByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script archived + content: + text/plain: + schema: + type: string + + /w/{workspace}/scripts/archive/h/{hash}: + post: + summary: archive script by hash + operationId: archiveScriptByHash + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptHash" + responses: + "200": + description: script details + content: + application/json: + schema: + $ref: "#/components/schemas/Script" + + /w/{workspace}/scripts/delete/h/{hash}: + post: + summary: delete script by hash (erase content but keep hash, require admin) + operationId: deleteScriptByHash + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptHash" + responses: + "200": + description: script details + content: + application/json: + schema: + $ref: "#/components/schemas/Script" + + /w/{workspace}/scripts/delete/p/{path}: + post: + summary: delete all scripts at a given path (require admin) + operationId: deleteScriptByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script path + content: + application/json: + schema: + type: string + + /w/{workspace}/scripts/get/p/{path}: + get: + summary: get script by path + operationId: getScriptByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - name: with_starred_info + in: query + schema: + type: boolean + responses: + "200": + description: script details + content: + application/json: + schema: + $ref: "#/components/schemas/Script" + + /w/{workspace}/scripts/get_triggers_count/{path}: + get: + summary: get triggers count of script + operationId: getTriggersCountOfScript + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: triggers count + content: + application/json: + schema: + $ref: "#/components/schemas/TriggersCount" + + /w/{workspace}/scripts/list_tokens/{path}: + get: + summary: get tokens with script scope + operationId: listTokensOfScript + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: tokens list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TruncatedToken" + + /w/{workspace}/scripts/get/draft/{path}: + get: + summary: get script by path with draft + operationId: getScriptByPathWithDraft + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script details + content: + application/json: + schema: + $ref: "#/components/schemas/NewScriptWithDraft" + + /w/{workspace}/scripts/history/p/{path}: + get: + summary: get history of a script by path + operationId: getScriptHistoryByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script history + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ScriptHistory" + + /w/{workspace}/scripts/get_latest_version/{path}: + get: + summary: get scripts's latest version (hash) + operationId: getScriptLatestVersion + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + tags: + - script + responses: + "200": + description: Script version/hash + content: + application/json: + + required: false + + schema: + $ref: "#/components/schemas/ScriptHistory" + + /w/{workspace}/scripts/history_update/h/{hash}/p/{path}: + post: + summary: update history of a script + operationId: updateScriptHistory + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptHash" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: Script deployment message + required: true + content: + application/json: + schema: + type: object + properties: + deployment_msg: + type: string + responses: + "200": + description: success + content: + text/plain: + schema: + type: string + + /w/{workspace}/scripts/raw/p/{path}: + get: + summary: raw script by path + operationId: rawScriptByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script content + content: + text/plain: + schema: + type: string + + /scripts_u/tokened_raw/{workspace}/{token}/{path}: + get: + summary: + raw script by path with a token (mostly used by lsp to be used with + import maps to resolve scripts) + operationId: rawScriptByPathTokened + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Token" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script content + content: + text/plain: + schema: + type: string + + /w/{workspace}/scripts/exists/p/{path}: + get: + summary: exists script by path + operationId: existsScriptByPath + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: does it exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/scripts/get/h/{hash}: + get: + summary: get script by hash + operationId: getScriptByHash + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptHash" + - name: with_starred_info + in: query + schema: + type: boolean + responses: + "200": + description: script details + content: + application/json: + schema: + $ref: "#/components/schemas/Script" + + /w/{workspace}/scripts/raw/h/{path}: + get: + summary: raw script by hash + operationId: rawScriptByHash + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: script content + content: + text/plain: + schema: + type: string + + /w/{workspace}/scripts/deployment_status/h/{hash}: + get: + summary: get script deployment status + operationId: getScriptDeploymentStatus + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptHash" + responses: + "200": + description: script details + content: + application/json: + schema: + type: object + properties: + lock: + type: string + lock_error_logs: + type: string + + /w/{workspace}/jobs/run/p/{path}: + post: + summary: run script by path + operationId: runScriptByPath + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - name: scheduled_for + description: when to schedule this job (leave empty for immediate run) + in: query + schema: + type: string + format: date-time + - name: scheduled_in_secs + description: schedule the script to execute in the number of seconds starting now + in: query + schema: + type: integer + - name: skip_preprocessor + description: skip the preprocessor + in: query + schema: + type: boolean + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/WorkerTag" + - $ref: "#/components/parameters/CacheTtl" + - $ref: "#/components/parameters/NewJobId" + - name: invisible_to_owner + description: make the run invisible to the the script owner (default false) + in: query + schema: + type: boolean + requestBody: + description: script args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/openai_sync/p/{path}: + post: + summary: run script by path in openai format + operationId: openaiSyncScriptByPath + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/NewJobId" + - $ref: "#/components/parameters/IncludeHeader" + - $ref: "#/components/parameters/QueueLimit" + + requestBody: + description: script args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "200": + description: job result + content: + application/json: + schema: {} + + /w/{workspace}/jobs/run_wait_result/p/{path}: + post: + summary: run script by path + operationId: runWaitResultScriptByPath + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/WorkerTag" + - $ref: "#/components/parameters/CacheTtl" + - $ref: "#/components/parameters/NewJobId" + - $ref: "#/components/parameters/IncludeHeader" + - $ref: "#/components/parameters/QueueLimit" + + requestBody: + description: script args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "200": + description: job result + content: + application/json: + schema: {} + + get: + summary: run script by path with get + operationId: runWaitResultScriptByPathGet + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/WorkerTag" + - $ref: "#/components/parameters/CacheTtl" + - $ref: "#/components/parameters/NewJobId" + - $ref: "#/components/parameters/IncludeHeader" + - $ref: "#/components/parameters/QueueLimit" + - $ref: "#/components/parameters/Payload" + + responses: + "200": + description: job result + content: + application/json: + schema: {} + + /w/{workspace}/jobs/openai_sync/f/{path}: + post: + summary: run flow by path and wait until completion in openai format + operationId: openaiSyncFlowByPath + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - $ref: "#/components/parameters/IncludeHeader" + - $ref: "#/components/parameters/QueueLimit" + - $ref: "#/components/parameters/NewJobId" + + requestBody: + description: script args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "200": + description: job result + content: + application/json: + schema: {} + + /w/{workspace}/jobs/run_wait_result/f/{path}: + post: + summary: run flow by path and wait until completion + operationId: runWaitResultFlowByPath + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - $ref: "#/components/parameters/IncludeHeader" + - $ref: "#/components/parameters/QueueLimit" + - $ref: "#/components/parameters/NewJobId" + + requestBody: + description: script args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "200": + description: job result + content: + application/json: + schema: {} + + /w/{workspace}/jobs/result_by_id/{flow_job_id}/{node_id}: + get: + summary: get job result by id + operationId: resultById + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: flow_job_id + in: path + required: true + schema: + type: string + - name: node_id + in: path + required: true + schema: + type: string + responses: + "200": + description: job result + content: + application/json: + schema: {} + + /w/{workspace}/flows/list_paths: + get: + summary: list all flow paths + operationId: listFlowPaths + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: list of flow paths + content: + text/plain: + schema: + type: array + items: + type: string + + /w/{workspace}/flows/list_search: + get: + summary: list flows for search + operationId: listSearchFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: flow list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + value: {} + required: + - path + - value + + /w/{workspace}/flows/list: + get: + summary: list all flows + operationId: listFlows + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - name: path_start + description: mask to filter matching starting path + in: query + schema: + type: string + - name: path_exact + description: mask to filter exact matching path + in: query + schema: + type: string + - name: show_archived + description: | + (default false) + show only the archived files. + when multiple archived hash share the same path, only the ones with the latest create_at + are displayed. + in: query + schema: + type: boolean + - name: starred_only + description: | + (default false) + show only the starred items + in: query + schema: + type: boolean + - name: include_draft_only + description: | + (default false) + include items that have no deployed version + in: query + schema: + type: boolean + - name: with_deployment_msg + description: | + (default false) + include deployment message + in: query + schema: + type: boolean + responses: + "200": + description: All flow + content: + application/json: + schema: + type: array + items: + allOf: + - $ref: "#/components/schemas/Flow" + - type: object + properties: + has_draft: + type: boolean + draft_only: + type: boolean + + /w/{workspace}/flows/history/p/{path}: + get: + summary: get flow history by path + operationId: getFlowHistory + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + tags: + - flow + responses: + "200": + description: Flow history + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/FlowVersion" + + /w/{workspace}/flows/get_latest_version/{path}: + get: + summary: get flow's latest version + operationId: getFlowLatestVersion + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + tags: + - flow + responses: + "200": + description: Flow version + content: + application/json: + required: false + + schema: + $ref: "#/components/schemas/FlowVersion" + + /w/{workspace}/flows/get/v/{version}/p/{path}: + get: + summary: get flow version + operationId: getFlowVersion + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - type: string + name: version + in: path + required: true + schema: + type: number + - $ref: "#/components/parameters/ScriptPath" + tags: + - flow + responses: + "200": + description: flow details + content: + application/json: + schema: + $ref: "#/components/schemas/Flow" + + /w/{workspace}/flows/history_update/v/{version}/p/{path}: + post: + summary: update flow history + operationId: updateFlowHistory + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - type: string + name: version + in: path + required: true + schema: + type: number + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: Flow deployment message + required: true + content: + application/json: + schema: + type: object + properties: + deployment_msg: + type: string + required: + - deployment_msg + tags: + - flow + responses: + "200": + description: success + content: + text/plain: + schema: + type: string + + + /w/{workspace}/flows/get/{path}: + get: + summary: get flow by path + operationId: getFlowByPath + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - name: with_starred_info + in: query + schema: + type: boolean + responses: + "200": + description: flow details + content: + application/json: + schema: + $ref: "#/components/schemas/Flow" + + /w/{workspace}/flows/get_triggers_count/{path}: + get: + summary: get triggers count of flow + operationId: getTriggersCountOfFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: triggers count + content: + application/json: + schema: + $ref: "#/components/schemas/TriggersCount" + + /w/{workspace}/flows/list_tokens/{path}: + get: + summary: get tokens with flow scope + operationId: listTokensOfFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: tokens list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TruncatedToken" + + + /w/{workspace}/flows/toggle_workspace_error_handler/{path}: + post: + summary: Toggle ON and OFF the workspace error handler for a given flow + operationId: toggleWorkspaceErrorHandlerForFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: Workspace error handler enabled + required: true + content: + application/json: + schema: + type: object + properties: + muted: + type: boolean + responses: + "200": + description: error handler toggled + content: + text/plain: + schema: + type: string + + /w/{workspace}/flows/get/draft/{path}: + get: + summary: get flow by path with draft + operationId: getFlowByPathWithDraft + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: flow details with draft + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Flow" + - type: object + properties: + draft: + $ref: "#/components/schemas/Flow" + + /w/{workspace}/flows/exists/{path}: + get: + summary: exists flow by path + operationId: existsFlowByPath + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: flow details + content: + application/json: + schema: + type: boolean + + /w/{workspace}/flows/create: + post: + summary: create flow + operationId: createFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Partially filled flow + required: true + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/OpenFlowWPath" + - type: object + properties: + draft_only: + type: boolean + deployment_message: + type: string + responses: + "201": + description: flow created + content: + text/plain: + schema: + type: string + + /w/{workspace}/flows/update/{path}: + post: + summary: update flow + operationId: updateFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: Partially filled flow + required: true + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/OpenFlowWPath" + - type: object + properties: + deployment_message: + type: string + + responses: + "200": + description: flow updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/flows/archive/{path}: + post: + summary: archive flow by path + operationId: archiveFlowByPath + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: archiveFlow + required: true + content: + application/json: + schema: + type: object + properties: + archived: + type: boolean + responses: + "200": + description: flow archived + content: + text/plain: + schema: + type: string + + /w/{workspace}/flows/delete/{path}: + delete: + summary: delete flow by path + operationId: deleteFlowByPath + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: flow delete + content: + text/plain: + schema: + type: string + + + /w/{workspace}/raw_apps/list: + get: + summary: list all raw apps + operationId: listRawApps + tags: + - raw_app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - name: path_start + description: mask to filter matching starting path + in: query + schema: + type: string + - name: path_exact + description: mask to filter exact matching path + in: query + schema: + type: string + - name: starred_only + description: | + (default false) + show only the starred items + in: query + schema: + type: boolean + responses: + "200": + description: All raw apps + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ListableRawApp" + + /w/{workspace}/raw_apps/exists/{path}: + get: + summary: does an app exisst at path + operationId: existsRawApp + tags: + - raw_app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: app exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/apps/get_data/{version}/{path}: + get: + summary: get app by path + operationId: getRawAppData + tags: + - raw_app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/VersionId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: app details + content: + text/javascript: + schema: + type: string + + /w/{workspace}/apps/list_search: + get: + summary: list apps for search + operationId: listSearchApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: app list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + value: {} + required: + - path + - value + + /w/{workspace}/apps/list: + get: + summary: list all apps + operationId: listApps + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - name: path_start + description: mask to filter matching starting path + in: query + schema: + type: string + - name: path_exact + description: mask to filter exact matching path + in: query + schema: + type: string + - name: starred_only + description: | + (default false) + show only the starred items + in: query + schema: + type: boolean + - name: include_draft_only + description: | + (default false) + include items that have no deployed version + in: query + schema: + type: boolean + - name: with_deployment_msg + description: | + (default false) + include deployment message + in: query + schema: + type: boolean + responses: + "200": + description: All apps + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ListableApp" + + /w/{workspace}/apps/create: + post: + summary: create app + operationId: createApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new app + required: true + content: + application/json: + schema: + type: object + properties: + path: + type: string + value: {} + summary: + type: string + policy: + $ref: "#/components/schemas/Policy" + draft_only: + type: boolean + deployment_message: + type: string + required: + - path + - value + - summary + - policy + responses: + "201": + description: app created + content: + text/plain: + schema: + type: string + + /w/{workspace}/apps/exists/{path}: + get: + summary: does an app exisst at path + operationId: existsApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: app exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/apps/get/p/{path}: + get: + summary: get app by path + operationId: getAppByPath + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - name: with_starred_info + in: query + schema: + type: boolean + responses: + "200": + description: app details + content: + application/json: + schema: + $ref: "#/components/schemas/AppWithLastVersion" + + /w/{workspace}/apps/get/draft/{path}: + get: + summary: get app by path with draft + operationId: getAppByPathWithDraft + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: app details with draft + content: + application/json: + schema: + $ref: "#/components/schemas/AppWithLastVersionWDraft" + + /w/{workspace}/apps/history/p/{path}: + get: + summary: get app history by path + operationId: getAppHistoryByPath + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: app history + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/AppHistory" + + /w/{workspace}/apps/get_latest_version/{path}: + get: + summary: get apps's latest version + operationId: getAppLatestVersion + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + tags: + - app + responses: + "200": + description: App version + content: + application/json: + required: false + schema: + $ref: "#/components/schemas/AppHistory" + + /w/{workspace}/apps/history_update/a/{id}/v/{version}: + post: + summary: update app history + operationId: updateAppHistory + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/PathId" + - $ref: "#/components/parameters/PathVersion" + requestBody: + description: App deployment message + required: true + content: + application/json: + schema: + type: object + properties: + deployment_msg: + type: string + responses: + "200": + description: success + content: + text/plain: + schema: + type: string + + /w/{workspace}/apps_u/public_app/{path}: + get: + summary: get public app by secret + operationId: getPublicAppBySecret + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: app details + content: + application/json: + schema: + $ref: "#/components/schemas/AppWithLastVersion" + + /w/{workspace}/apps_u/public_resource/{path}: + get: + summary: get public resource + operationId: get public resource + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: resource value + content: + application/json: + schema: {} + + /w/{workspace}/apps/secret_of/{path}: + get: + summary: get public secret of app + operationId: getPublicSecretOfApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: app secret + content: + text/plain: + schema: + type: string + + /w/{workspace}/apps/get/v/{id}: + get: + summary: get app by version + operationId: getAppByVersion + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/PathId" + responses: + "200": + description: app details + content: + application/json: + schema: + $ref: "#/components/schemas/AppWithLastVersion" + + /w/{workspace}/raw_apps/create: + post: + summary: create raw app + operationId: createRawApp + tags: + - raw_app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new raw app + required: true + content: + application/json: + schema: + type: object + properties: + path: + type: string + value: + type: string + summary: + type: string + required: + - path + - value + - summary + responses: + "201": + description: raw app created + content: + text/plain: + schema: + type: string + + /w/{workspace}/raw_apps/update/{path}: + post: + summary: update app + operationId: updateRawApp + tags: + - raw_app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: updateraw app + required: true + content: + application/json: + schema: + type: object + properties: + path: + type: string + summary: + type: string + value: + type: string + responses: + "200": + description: app updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/raw_apps/delete/{path}: + delete: + summary: delete raw app + operationId: deleteRawApp + tags: + - raw_app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: app deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/apps/delete/{path}: + delete: + summary: delete app + operationId: deleteApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: app deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/apps/update/{path}: + post: + summary: update app + operationId: updateApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: update app + required: true + content: + application/json: + schema: + type: object + properties: + path: + type: string + summary: + type: string + value: {} + policy: + $ref: "#/components/schemas/Policy" + deployment_message: + type: string + responses: + "200": + description: app updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/apps_u/execute_component/{path}: + post: + summary: executeComponent + operationId: executeComponent + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: update app + required: true + content: + application/json: + schema: + type: object + properties: + component: + type: string + #script: script/ + #flow: flow/ + path: + type: string + args: {} + raw_code: + type: object + properties: + content: + type: string + language: + type: string + path: + type: string + lock: + type: string + cache_ttl: + type: integer + required: + - content + - language + force_viewer_static_fields: + type: object + force_viewer_one_of_fields: + type: object + force_viewer_allow_user_resources: + type: array + items: + type: string + required: + - args + - component + + responses: + "200": + description: job uuid + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs/run/f/{path}: + post: + summary: run flow by path + operationId: runFlowByPath + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + - name: scheduled_for + description: when to schedule this job (leave empty for immediate run) + in: query + schema: + type: string + format: date-time + - name: scheduled_in_secs + description: schedule the script to execute in the number of seconds starting now + in: query + schema: + type: integer + - name: skip_preprocessor + description: skip the preprocessor + in: query + schema: + type: boolean + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/WorkerTag" + - $ref: "#/components/parameters/NewJobId" + - $ref: "#/components/parameters/IncludeHeader" + - name: invisible_to_owner + description: make the run invisible to the the flow owner (default false) + in: query + schema: + type: boolean + + requestBody: + description: flow args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}: + post: + summary: restart a completed flow at a given step + operationId: restartFlowAtStep + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: step_id + description: step id to restart the flow from + required: true + in: path + schema: + type: string + - name: branch_or_iteration_n + description: + for branchall or loop, the iteration at which the flow should + restart + required: true + in: path + schema: + type: integer + - name: scheduled_for + description: when to schedule this job (leave empty for immediate run) + in: query + schema: + type: string + format: date-time + - name: scheduled_in_secs + description: schedule the script to execute in the number of seconds starting now + in: query + schema: + type: integer + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/WorkerTag" + - $ref: "#/components/parameters/NewJobId" + - $ref: "#/components/parameters/IncludeHeader" + - name: invisible_to_owner + description: make the run invisible to the the flow owner (default false) + in: query + schema: + type: boolean + + requestBody: + description: flow args + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScriptArgs" + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/run/h/{hash}: + post: + summary: run script by hash + operationId: runScriptByHash + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptHash" + - name: scheduled_for + description: when to schedule this job (leave empty for immediate run) + in: query + schema: + type: string + format: date-time + - name: scheduled_in_secs + description: schedule the script to execute in the number of seconds starting now + in: query + schema: + type: integer + - name: skip_preprocessor + description: skip the preprocessor + in: query + schema: + type: boolean + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/WorkerTag" + - $ref: "#/components/parameters/CacheTtl" + - $ref: "#/components/parameters/NewJobId" + - $ref: "#/components/parameters/IncludeHeader" + - name: invisible_to_owner + description: make the run invisible to the the script owner (default false) + in: query + schema: + type: boolean + requestBody: + description: Partially filled args + required: true + content: + application/json: + schema: + type: object + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/run/preview: + post: + summary: run script preview + operationId: runScriptPreview + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/IncludeHeader" + - name: invisible_to_owner + description: make the run invisible to the the script owner (default false) + in: query + schema: + type: boolean + - $ref: "#/components/parameters/NewJobId" + + requestBody: + description: preview + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Preview" + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/workflow_as_code/{job_id}/{entrypoint}: + post: + summary: run code-workflow task + operationId: runCodeWorkflowTask + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + - name: job_id + in: path + required: true + schema: + type: string + - name: entrypoint + in: path + required: true + schema: + type: string + + requestBody: + description: preview + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WorkflowTask" + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/run/dependencies: + post: + summary: run a one-off dependencies job + operationId: runRawScriptDependencies + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + requestBody: + description: raw script content + required: true + content: + application/json: + schema: + type: object + properties: + raw_scripts: + type: array + items: + $ref: "#/components/schemas/RawScriptForDependencies" + entrypoint: + type: string + required: + - entrypoint + - raw_scripts + responses: + "201": + description: dependency job result + content: + application/json: + schema: + type: object + properties: + lock: + type: string + required: + - lock + + /w/{workspace}/jobs/run/preview_flow: + post: + summary: run flow preview + operationId: runFlowPreview + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/IncludeHeader" + - name: invisible_to_owner + description: make the run invisible to the the script owner (default false) + in: query + schema: + type: boolean + - $ref: "#/components/parameters/NewJobId" + + requestBody: + description: preview + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/FlowPreview" + + responses: + "201": + description: job created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/jobs/queue/list: + get: + summary: list all queued jobs + operationId: listQueue + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/ScriptExactPath" + - $ref: "#/components/parameters/ScriptStartPath" + - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/ScriptExactHash" + - $ref: "#/components/parameters/StartedBefore" + - $ref: "#/components/parameters/StartedAfter" + - $ref: "#/components/parameters/Success" + - $ref: "#/components/parameters/ScheduledForBeforeNow" + - $ref: "#/components/parameters/JobKinds" + - $ref: "#/components/parameters/Suspended" + - $ref: "#/components/parameters/Running" + - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Tag" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean + - name: is_not_schedule + description: is not a scheduled job + in: query + schema: + type: boolean + responses: + "200": + description: All queued jobs + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/QueuedJob" + + /w/{workspace}/jobs/queue/count: + get: + summary: get queue count + operationId: getQueueCount + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean + responses: + "200": + description: queue count + content: + application/json: + schema: + type: object + properties: + database_length: + type: integer + suspended: + type: integer + required: + - database_length + + /w/{workspace}/jobs/completed/count: + get: + summary: get completed count + operationId: getCompletedCount + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: completed count + content: + application/json: + schema: + type: object + properties: + database_length: + type: integer + required: + - database_length + + /w/{workspace}/jobs/completed/count_jobs: + get: + summary: count number of completed jobs with filter + operationId: countCompletedJobs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: completed_after_s_ago + in: query + schema: + type: integer + - name: success + in: query + schema: + type: boolean + - name: tags + in: query + schema: + type: string + - name: all_workspaces + in: query + schema: + type: boolean + responses: + "200": + description: Count of completed jobs + content: + application/json: + schema: + type: integer + + + /w/{workspace}/jobs/queue/list_filtered_uuids: + get: + summary: get the ids of all jobs matching the given filters + operationId: listFilteredUuids + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/ScriptExactPath" + - $ref: "#/components/parameters/ScriptStartPath" + - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/ScriptExactHash" + - $ref: "#/components/parameters/StartedBefore" + - $ref: "#/components/parameters/StartedAfter" + - $ref: "#/components/parameters/Success" + - $ref: "#/components/parameters/ScheduledForBeforeNow" + - $ref: "#/components/parameters/JobKinds" + - $ref: "#/components/parameters/Suspended" + - $ref: "#/components/parameters/Running" + - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Tag" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: concurrency_key + in: query + required: false + schema: + type: string + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean + - name: is_not_schedule + description: is not a scheduled job + in: query + schema: + type: boolean + responses: + "200": + description: uuids of jobs + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/jobs/queue/cancel_selection: + post: + summary: cancel jobs based on the given uuids + operationId: cancelSelection + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: uuids of the jobs to cancel + required: true + content: + application/json: + schema: + type: array + items: + type: string + responses: + "200": + description: uuids of canceled jobs + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/jobs/completed/list: + get: + summary: list all completed jobs + operationId: listCompletedJobs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/OrderDesc" + - $ref: "#/components/parameters/CreatedBy" + - $ref: "#/components/parameters/Label" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/ScriptExactPath" + - $ref: "#/components/parameters/ScriptStartPath" + - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/ScriptExactHash" + - $ref: "#/components/parameters/StartedBefore" + - $ref: "#/components/parameters/StartedAfter" + - $ref: "#/components/parameters/Success" + - $ref: "#/components/parameters/JobKinds" + - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Tag" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: is_skipped + description: is the job skipped + in: query + schema: + type: boolean + - name: is_flow_step + description: is the job a flow step + in: query + schema: + type: boolean + - name: has_null_parent + description: has null parent + in: query + schema: + type: boolean + - name: is_not_schedule + description: is not a scheduled job + in: query + schema: + type: boolean + responses: + "200": + description: All completed jobs + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/CompletedJob" + + /w/{workspace}/jobs/list: + get: + summary: list all jobs + operationId: listJobs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/CreatedBy" + - $ref: "#/components/parameters/Label" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/ScriptExactPath" + - $ref: "#/components/parameters/ScriptStartPath" + - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/ScriptExactHash" + - $ref: "#/components/parameters/StartedBefore" + - $ref: "#/components/parameters/StartedAfter" + - $ref: "#/components/parameters/CreatedBefore" + - $ref: "#/components/parameters/CreatedAfter" + - $ref: "#/components/parameters/CreatedOrStartedBefore" + - $ref: "#/components/parameters/Running" + - $ref: "#/components/parameters/ScheduledForBeforeNow" + - $ref: "#/components/parameters/CreatedOrStartedAfter" + - $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob" + - $ref: "#/components/parameters/JobKinds" + - $ref: "#/components/parameters/Suspended" + - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/Tag" + - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: is_skipped + description: is the job skipped + in: query + schema: + type: boolean + - name: is_flow_step + description: is the job a flow step + in: query + schema: + type: boolean + - name: has_null_parent + description: has null parent + in: query + schema: + type: boolean + - name: success + description: filter on successful jobs + in: query + schema: + type: boolean + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean + - name: is_not_schedule + description: is not a scheduled job + in: query + schema: + type: boolean + responses: + "200": + description: All jobs + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Job" + + /jobs/db_clock: + get: + summary: get db clock + operationId: getDbClock + tags: + - job + responses: + "200": + description: the timestamp of the db that can be used to compute the drift + content: + application/json: + schema: + type: integer + + /jobs/completed/count_by_tag: + get: + summary: Count jobs by tag + operationId: countJobsByTag + tags: + - job + parameters: + - name: horizon_secs + in: query + description: Past Time horizon in seconds (when to start the count = now - horizon) (default is 3600) + required: false + schema: + type: integer + - name: workspace_id + in: query + description: Specific workspace ID to filter results (optional) + required: false + schema: + type: string + responses: + "200": + description: Job counts by tag + content: + application/json: + schema: + type: array + items: + type: object + properties: + tag: + type: string + count: + type: integer + required: + - tag + - count + + /w/{workspace}/jobs_u/get/{id}: + get: + summary: get job + operationId: getJob + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: no_logs + in: query + schema: + type: boolean + responses: + "200": + description: job details + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + + /w/{workspace}/jobs_u/get_root_job_id/{id}: + get: + summary: get root job id + operationId: getRootJobId + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: get root job id + content: + application/json: + schema: + type: string + + /w/{workspace}/jobs_u/get_logs/{id}: + get: + summary: get job logs + operationId: getJob logs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: job details + content: + text/plain: + schema: + type: string + + + /w/{workspace}/jobs_u/get_args/{id}: + get: + summary: get job args + operationId: getJobArgs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: job args + content: + application/json: + schema: {} + + /w/{workspace}/jobs_u/getupdate/{id}: + get: + summary: get job updates + operationId: getJobUpdates + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: running + in: query + schema: + type: boolean + - name: log_offset + in: query + schema: + type: integer + - name: get_progress + in: query + schema: + type: boolean + + responses: + "200": + description: job details + content: + application/json: + schema: + type: object + properties: + running: + type: boolean + completed: + type: boolean + new_logs: + type: string + log_offset: + type: integer + mem_peak: + type: integer + progress: + type: integer + flow_status: + $ref: "#/components/schemas/WorkflowStatusRecord" + + /w/{workspace}/jobs_u/get_log_file/{path}: + get: + summary: get log file from object store + operationId: getLogFileFromStore + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: path + in: path + required: true + schema: + type: string + responses: + "200": + description: job log + content: + text/plain: + type: string + + /w/{workspace}/jobs_u/get_flow_debug_info/{id}: + get: + summary: get flow debug info + operationId: getFlowDebugInfo + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: flow debug info details + content: + application/json: + schema: {} + + /w/{workspace}/jobs_u/completed/get/{id}: + get: + summary: get completed job + operationId: getCompletedJob + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: job details + content: + application/json: + schema: + $ref: "#/components/schemas/CompletedJob" + + /w/{workspace}/jobs_u/completed/get_result/{id}: + get: + summary: get completed job result + operationId: getCompletedJobResult + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: suspended_job + in: query + schema: + type: string + - name: resume_id + in: query + schema: + type: integer + - name: secret + in: query + schema: + type: string + - name: approver + in: query + schema: + type: string + responses: + "200": + description: result + content: + application/json: + schema: {} + + /w/{workspace}/jobs_u/completed/get_result_maybe/{id}: + get: + summary: get completed job result if job is completed + operationId: getCompletedJobResultMaybe + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - $ref: "#/components/parameters/GetStarted" + + responses: + "200": + description: result + content: + application/json: + schema: + type: object + properties: + completed: + type: boolean + result: {} + success: + type: boolean + started: + type: boolean + required: + - completed + - result + + /w/{workspace}/jobs/completed/delete/{id}: + post: + summary: delete completed job (erase content but keep run id) + operationId: deleteCompletedJob + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: job details + content: + application/json: + schema: + $ref: "#/components/schemas/CompletedJob" + + /w/{workspace}/jobs_u/queue/cancel/{id}: + post: + summary: cancel queued or running job + operationId: cancelQueuedJob + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + requestBody: + description: reason + required: true + content: + application/json: + schema: + type: object + properties: + reason: + type: string + + responses: + "200": + description: job canceled + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs_u/queue/cancel_persistent/{path}: + post: + summary: cancel all queued jobs for persistent script + operationId: cancelPersistentQueuedJobs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: reason + required: true + content: + application/json: + schema: + type: object + properties: + reason: + type: string + + responses: + "200": + description: persistent job scaled down to zero + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs_u/queue/force_cancel/{id}: + post: + summary: force cancel queued job + operationId: forceCancelQueuedJob + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + requestBody: + description: reason + required: true + content: + application/json: + schema: + type: object + properties: + reason: + type: string + + responses: + "200": + description: job canceled + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs/job_signature/{id}/{resume_id}: + get: + summary: create an HMac signature given a job id and a resume id + operationId: createJobSignature + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: approver + in: query + schema: + type: string + responses: + "200": + description: job signature + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs/resume_urls/{id}/{resume_id}: + get: + summary: get resume urls given a job_id, resume_id and a nonce to resume a flow + operationId: getResumeUrls + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: approver + in: query + schema: + type: string + responses: + "200": + description: url endpoints + content: + application/json: + schema: + type: object + properties: + approvalPage: + type: string + resume: + type: string + cancel: + type: string + required: + - approvalPage + - resume + - cancel + + /w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}: + get: + summary: resume a job for a suspended flow + operationId: resumeSuspendedJobGet + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - $ref: "#/components/parameters/Payload" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: signature + in: path + required: true + schema: + type: string + - name: approver + in: query + schema: + type: string + responses: + "201": + description: job resumed + content: + text/plain: + schema: + type: string + + post: + summary: resume a job for a suspended flow + operationId: resumeSuspendedJobPost + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: signature + in: path + required: true + schema: + type: string + - name: approver + in: query + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + "201": + description: job resumed + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs/flow/user_states/{id}/{key}: + post: + summary: set flow user state at a given key + operationId: setFlowUserState + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: key + in: path + required: true + schema: + type: string + requestBody: + description: new value + required: true + content: + application/json: + schema: {} + responses: + "200": + description: flow user state updated + content: + text/plain: + schema: + type: string + get: + summary: get flow user state at a given key + operationId: getFlowUserState + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: key + in: path + required: true + schema: + type: string + responses: + "200": + description: flow user state updated + content: + application/json: + schema: {} + + /w/{workspace}/jobs/flow/resume/{id}: + post: + summary: resume a job for a suspended flow as an owner + operationId: resumeSuspendedFlowAsOwner + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + "201": + description: job resumed + content: + text/plain: + schema: + type: string + + + /w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}: + get: + summary: cancel a job for a suspended flow + operationId: cancelSuspendedJobGet + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: signature + in: path + required: true + schema: + type: string + - name: approver + in: query + schema: + type: string + responses: + "201": + description: job canceled + content: + text/plain: + schema: + type: string + + post: + summary: cancel a job for a suspended flow + operationId: cancelSuspendedJobPost + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: signature + in: path + required: true + schema: + type: string + - name: approver + in: query + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + "201": + description: job canceled + content: + text/plain: + schema: + type: string + + /w/{workspace}/jobs_u/get_flow/{id}/{resume_id}/{signature}: + get: + summary: get parent flow job of suspended job + operationId: getSuspendedJobFlow + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + - name: resume_id + in: path + required: true + schema: + type: integer + - name: signature + in: path + required: true + schema: + type: string + - name: approver + in: query + schema: + type: string + responses: + "200": + description: parent flow details + content: + application/json: + schema: + type: object + properties: + job: + $ref: "#/components/schemas/Job" + approvers: + type: array + items: + type: object + properties: + resume_id: + type: integer + approver: + type: string + required: + - resume_id + - approver + required: + - job + - approvers + + /schedules/preview: + post: + summary: preview schedule + operationId: previewSchedule + tags: + - schedule + requestBody: + description: schedule + required: true + content: + application/json: + schema: + type: object + properties: + schedule: + type: string + timezone: + type: string + required: + - schedule + - timezone + responses: + "200": + description: List of 5 estimated upcoming execution events (in UTC) + content: + application/json: + schema: + type: array + items: + type: string + format: date-time + + /w/{workspace}/schedules/create: + post: + summary: create schedule + operationId: createSchedule + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new schedule + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewSchedule" + responses: + "201": + description: schedule created + content: + text/plain: + schema: + type: string + + /w/{workspace}/schedules/update/{path}: + post: + summary: update schedule + operationId: updateSchedule + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated schedule + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditSchedule" + responses: + "200": + description: schedule updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/schedules/setenabled/{path}: + post: + summary: set enabled schedule + operationId: setScheduleEnabled + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated schedule enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + + responses: + "200": + description: schedule enabled set + content: + text/plain: + schema: + type: string + + /w/{workspace}/schedules/delete/{path}: + delete: + summary: delete schedule + operationId: deleteSchedule + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: schedule deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/schedules/get/{path}: + get: + summary: get schedule + operationId: getSchedule + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: schedule deleted + content: + application/json: + schema: + $ref: "#/components/schemas/Schedule" + + /w/{workspace}/schedules/exists/{path}: + get: + summary: does schedule exists + operationId: existsSchedule + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: schedule exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/schedules/list: + get: + summary: list schedules + operationId: listSchedules + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/ArgsFilter" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: schedule list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Schedule" + + /w/{workspace}/schedules/list_with_jobs: + get: + summary: list schedules with last 20 jobs + operationId: listSchedulesWithJobs + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: schedule list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ScheduleWJobs" + + /w/{workspace}/schedules/setdefaulthandler: + post: + summary: Set default error or recoevery handler + operationId: setDefaultErrorOrRecoveryHandler + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Handler description + required: true + content: + application/json: + schema: + type: object + properties: + handler_type: + type: string + enum: ["error", "recovery", "success"] + override_existing: + type: boolean + path: + type: string + extra_args: + type: object + number_of_occurence: + type: integer + number_of_occurence_exact: + type: boolean + workspace_handler_muted: + type: boolean + required: + - handler_type + - override_existing + responses: + "201": + description: default error handler set + + + /w/{workspace}/http_triggers/create: + post: + summary: create http trigger + operationId: createHttpTrigger + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new http trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewHttpTrigger" + responses: + "201": + description: http trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/http_triggers/update/{path}: + post: + summary: update http trigger + operationId: updateHttpTrigger + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditHttpTrigger" + responses: + "200": + description: http trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/http_triggers/delete/{path}: + delete: + summary: delete http trigger + operationId: deleteHttpTrigger + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: http trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/http_triggers/get/{path}: + get: + summary: get http trigger + operationId: getHttpTrigger + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: http trigger deleted + content: + application/json: + schema: + $ref: "#/components/schemas/HttpTrigger" + + + /w/{workspace}/http_triggers/list: + get: + summary: list http triggers + operationId: listHttpTriggers + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: http trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/HttpTrigger" + + + /w/{workspace}/http_triggers/exists/{path}: + get: + summary: does http trigger exists + operationId: existsHttpTrigger + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: http trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/http_triggers/route_exists: + post: + summary: does route exists + operationId: existsRoute + tags: + - http_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: route exists request + required: true + content: + application/json: + schema: + type: object + properties: + route_path: + type: string + http_method: + type: string + enum: ["get", "post", "put", "delete", "patch"] + required: + - kind + - route_path + - http_method + responses: + "200": + description: route exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/websocket_triggers/create: + post: + summary: create websocket trigger + operationId: createWebsocketTrigger + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new websocket trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewWebsocketTrigger" + responses: + "201": + description: websocket trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/websocket_triggers/update/{path}: + post: + summary: update websocket trigger + operationId: updateWebsocketTrigger + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditWebsocketTrigger" + responses: + "200": + description: websocket trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/websocket_triggers/delete/{path}: + delete: + summary: delete websocket trigger + operationId: deleteWebsocketTrigger + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: websocket trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/websocket_triggers/get/{path}: + get: + summary: get websocket trigger + operationId: getWebsocketTrigger + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: websocket trigger deleted + content: + application/json: + schema: + $ref: "#/components/schemas/WebsocketTrigger" + + + /w/{workspace}/websocket_triggers/list: + get: + summary: list websocket triggers + operationId: listWebsocketTriggers + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: websocket trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/WebsocketTrigger" + + + /w/{workspace}/websocket_triggers/exists/{path}: + get: + summary: does websocket trigger exists + operationId: existsWebsocketTrigger + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: websocket trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/websocket_triggers/setenabled/{path}: + post: + summary: set enabled websocket trigger + operationId: setWebsocketTriggerEnabled + tags: + - websocket_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated websocket trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: websocket trigger enabled set + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/create: + post: + summary: create database trigger + operationId: createDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new database trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewDatabaseTrigger" + responses: + "201": + description: database trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/update/{path}: + post: + summary: update database trigger + operationId: updateDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditDatabaseTrigger" + responses: + "200": + description: database trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/delete/{path}: + delete: + summary: delete database trigger + operationId: deleteDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: database trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/database_triggers/list: + get: + summary: list database triggers + operationId: listDatabaseTriggers + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: database trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DatabaseTrigger" + + /w/{workspace}/database_triggers/exists/{path}: + get: + summary: does database trigger exists + operationId: existsDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: database trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/database_triggers/setenabled/{path}: + post: + summary: set enabled database trigger + operationId: setDatabaseTriggerEnabled + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated database trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: database trigger enabled set + content: + text/plain: + schema: + type: string + + /groups/list: + get: + summary: list instance groups + operationId: listInstanceGroups + tags: + - group + responses: + "200": + description: instance group list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/InstanceGroup" + + /groups/get/{name}: + get: + summary: get instance group + operationId: getInstanceGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/Name" + responses: + "200": + description: instance group + content: + application/json: + schema: + $ref: "#/components/schemas/InstanceGroup" + + /groups/create: + post: + summary: create instance group + operationId: createInstanceGroup + tags: + - group + requestBody: + description: create instance group + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + summary: + type: string + required: + - name + responses: + "200": + description: instance group created + content: + text/plain: + schema: + type: string + + /groups/update/{name}: + post: + summary: update instance group + operationId: updateInstanceGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/Name" + requestBody: + description: update instance group + required: true + content: + application/json: + schema: + type: object + properties: + new_summary: + type: string + required: + - new_summary + responses: + "200": + description: instance group updated + content: + text/plain: + schema: + type: string + + /groups/delete/{name}: + delete: + summary: delete instance group + operationId: deleteInstanceGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/Name" + responses: + "200": + description: instance group deleted + content: + text/plain: + schema: + type: string + + /groups/adduser/{name}: + post: + summary: add user to instance group + operationId: addUserToInstanceGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/Name" + requestBody: + description: user to add to instance group + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + required: + - email + responses: + "200": + description: user added to instance group + content: + text/plain: + schema: + type: string + + /groups/removeuser/{name}: + post: + summary: remove user from instance group + operationId: removeUserFromInstanceGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/Name" + requestBody: + description: user to remove from instance group + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + required: + - email + responses: + "200": + description: user removed from instance group + content: + text/plain: + schema: + type: string + /groups/export: + get: + summary: export instance groups + operationId: exportInstanceGroups + tags: + - group + responses: + "200": + description: exported instance groups + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ExportedInstanceGroup" + + /groups/overwrite: + post: + summary: overwrite instance groups + operationId: overwriteInstanceGroups + tags: + - group + requestBody: + description: overwrite instance groups + required: true + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ExportedInstanceGroup" + responses: + "200": + description: success message + content: + text/plain: + schema: + type: string + + /w/{workspace}/groups/list: + get: + summary: list groups + operationId: listGroups + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: group list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Group" + + /w/{workspace}/groups/listnames: + get: + summary: list group names + operationId: listGroupNames + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: only_member_of + in: query + description: only list the groups the user is member of (default false) + schema: + type: boolean + responses: + "200": + description: group list + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/groups/create: + post: + summary: create group + operationId: createGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: create group + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + summary: + type: string + required: + - name + responses: + "200": + description: group created + content: + text/plain: + schema: + type: string + + /w/{workspace}/groups/update/{name}: + post: + summary: update group + operationId: updateGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + requestBody: + description: updated group + required: true + content: + application/json: + schema: + type: object + properties: + summary: + type: string + responses: + "200": + description: group updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/groups/delete/{name}: + delete: + summary: delete group + operationId: deleteGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: group deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/groups/get/{name}: + get: + summary: get group + operationId: getGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: group + content: + application/json: + schema: + $ref: "#/components/schemas/Group" + + /w/{workspace}/groups/adduser/{name}: + post: + summary: add user to group + operationId: addUserToGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + requestBody: + description: added user to group + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + responses: + "200": + description: user added to group + content: + text/plain: + schema: + type: string + + /w/{workspace}/groups/removeuser/{name}: + post: + summary: remove user to group + operationId: removeUserToGroup + tags: + - group + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + requestBody: + description: added user to group + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + responses: + "200": + description: user removed from group + content: + text/plain: + schema: + type: string + + /w/{workspace}/folders/list: + get: + summary: list folders + operationId: listFolders + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: folder list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Folder" + + /w/{workspace}/folders/listnames: + get: + summary: list folder names + operationId: listFolderNames + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: only_member_of + in: query + description: only list the folders the user is member of (default false) + schema: + type: boolean + responses: + "200": + description: folder list + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/folders/create: + post: + summary: create folder + operationId: createFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: create folder + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + summary: + type: string + owners: + type: array + items: + type: string + extra_perms: + additionalProperties: + type: boolean + required: + - name + responses: + "200": + description: folder created + content: + text/plain: + schema: + type: string + + /w/{workspace}/folders/update/{name}: + post: + summary: update folder + operationId: updateFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + requestBody: + description: update folder + required: true + content: + application/json: + schema: + type: object + properties: + summary: + type: string + owners: + type: array + items: + type: string + extra_perms: + additionalProperties: + type: boolean + responses: + "200": + description: folder updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/folders/delete/{name}: + delete: + summary: delete folder + operationId: deleteFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: folder deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/folders/get/{name}: + get: + summary: get folder + operationId: getFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: folder + content: + application/json: + schema: + $ref: "#/components/schemas/Folder" + + /w/{workspace}/folders/getusage/{name}: + get: + summary: get folder usage + operationId: getFolderUsage + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: folder + content: + application/json: + schema: + type: object + properties: + scripts: + type: number + flows: + type: number + apps: + type: number + resources: + type: number + variables: + type: number + schedules: + type: number + required: + - scripts + - flows + - apps + - resources + - variables + - schedules + + /w/{workspace}/folders/addowner/{name}: + post: + summary: add owner to folder + operationId: addOwnerToFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + requestBody: + description: owner user to folder + required: true + content: + application/json: + schema: + type: object + properties: + owner: + type: string + required: + - owner + + responses: + "200": + description: owner added to folder + content: + text/plain: + schema: + type: string + + /w/{workspace}/folders/removeowner/{name}: + post: + summary: remove owner to folder + operationId: removeOwnerToFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + requestBody: + description: added owner to folder + required: true + content: + application/json: + schema: + type: object + properties: + owner: + type: string + write: + type: boolean + required: + - owner + responses: + "200": + description: owner removed from folder + content: + text/plain: + schema: + type: string + + /workers/list: + get: + summary: list workers + operationId: listWorkers + tags: + - worker + parameters: + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: ping_since + in: query + required: false + description: number of seconds the worker must have had a last ping more recent of (default to 300) + schema: + type: integer + responses: + "200": + description: a list of workers + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/WorkerPing" + + /workers/exists_worker_with_tag: + get: + summary: exists worker with tag + operationId: existsWorkerWithTag + tags: + - worker + parameters: + - name: tag + in: query + required: true + schema: + type: string + responses: + "200": + description: whether a worker with the tag exists + content: + application/json: + schema: + type: boolean + + /workers/queue_metrics: + get: + summary: get queue metrics + operationId: getQueueMetrics + tags: + - worker + responses: + "200": + description: metrics + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: string + values: + type: array + items: + type: object + properties: + created_at: + type: string + value: + type: number + required: + - created_at + - value + required: + - id + - values + + /workers/queue_counts: + get: + summary: get counts of jobs waiting for an executor per tag + operationId: getCountsOfJobsWaitingPerTag + tags: + - worker + responses: + "200": + description: queue counts + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + + + /configs/list_worker_groups: + get: + summary: list worker groups + operationId: listWorkerGroups + tags: + - config + responses: + "200": + description: a list of worker group configs + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + config: {} + required: + - name + - config + + /configs/get/{name}: + get: + summary: get config + operationId: get config + tags: + - config + parameters: + - $ref: "#/components/parameters/Name" + responses: + "200": + description: a config + content: + application/json: + schema: {} + + /configs/update/{name}: + post: + summary: Update config + operationId: updateConfig + tags: + - config + parameters: + - $ref: "#/components/parameters/Name" + requestBody: + description: worker group + required: true + content: + application/json: + schema: {} + responses: + "200": + description: Update a worker group + content: + text/plain: + schema: + type: string + delete: + summary: Delete Config + operationId: deleteConfig + tags: + - config + parameters: + - $ref: "#/components/parameters/Name" + responses: + "200": + description: Delete config + content: + text/plain: + schema: + type: string + + /configs/list: + get: + summary: list configs + operationId: listConfigs + tags: + - config + responses: + "200": + description: list of configs + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Config" + + /configs/list_autoscaling_events/{worker_group}: + get: + summary: List autoscaling events + operationId: listAutoscalingEvents + tags: + - config + parameters: + - name: worker_group + in: path + required: true + schema: + type: string + responses: + "200": + description: List of autoscaling events + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/AutoscalingEvent" + + + /w/{workspace}/acls/get/{kind}/{path}: + get: + summary: get granular acls + operationId: getGranularAcls + tags: + - granular_acl + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: kind + in: path + required: true + schema: + type: string + enum: + [ + script, + group_, + resource, + schedule, + variable, + flow, + folder, + app, + raw_app, + http_trigger, + websocket_trigger, + database_trigger + ] + responses: + "200": + description: acls + content: + application/json: + schema: + type: object + additionalProperties: + type: boolean + + /w/{workspace}/acls/add/{kind}/{path}: + post: + summary: add granular acls + operationId: addGranularAcls + tags: + - granular_acl + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: kind + in: path + required: true + schema: + type: string + enum: + [ + script, + group_, + resource, + schedule, + variable, + flow, + folder, + app, + raw_app, + http_trigger, + websocket_trigger, + database_trigger + ] + requestBody: + description: acl to add + required: true + content: + application/json: + schema: + type: object + properties: + owner: + type: string + write: + type: boolean + required: [owner] + responses: + "200": + description: granular acl added + content: + text/plain: + schema: + type: string + + /w/{workspace}/acls/remove/{kind}/{path}: + post: + summary: remove granular acls + operationId: removeGranularAcls + tags: + - granular_acl + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: kind + in: path + required: true + schema: + type: string + enum: + [ + script, + group_, + resource, + schedule, + variable, + flow, + folder, + app, + raw_app, + http_trigger, + websocket_trigger, + database_trigger + ] + requestBody: + description: acl to add + required: true + content: + application/json: + schema: + type: object + properties: + owner: + type: string + required: [owner] + responses: + "200": + description: granular acl removed + content: + text/plain: + schema: + type: string + + /w/{workspace}/capture_u/{path}: + post: + summary: update flow preview capture + operationId: updateCapture + tags: + - capture + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "204": + description: flow preview captured + + /w/{workspace}/capture/{path}: + put: + summary: create flow preview capture + operationId: createCapture + tags: + - capture + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "201": + description: flow preview capture created + get: + summary: get flow preview capture + operationId: getCapture + tags: + - capture + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: captured flow preview + content: + application/json: + schema: {} + "404": + description: capture does not exist for this flow + + /w/{workspace}/favorites/star: + post: + summary: star item + operationId: star + tags: + - favorite + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + path: + type: string + favorite_kind: + type: string + enum: [flow, app, script, raw_app] + responses: + "200": + description: star item + + /w/{workspace}/favorites/unstar: + post: + summary: unstar item + operationId: unstar + tags: + - favorite + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + path: + type: string + favorite_kind: + type: string + enum: [flow, app, script, raw_app] + responses: + "200": + description: unstar item + + /w/{workspace}/inputs/history: + get: + summary: List Inputs used in previously completed jobs + operationId: getInputHistory + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableId" + - $ref: "#/components/parameters/RunnableTypeQuery" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: Input history for completed jobs + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Input" + + /w/{workspace}/inputs/{jobOrInputId}/args: + get: + summary: Get args from history or saved input + operationId: getArgsFromHistoryOrSavedInput + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: jobOrInputId + in: path + required: true + schema: + type: string + - name: input + in: query + schema: + type: boolean + - name: allow_large + in: query + schema: + type: boolean + responses: + "200": + description: args + content: + application/json: + schema: {} + + /w/{workspace}/inputs/list: + get: + summary: List saved Inputs for a Runnable + operationId: listInputs + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableId" + - $ref: "#/components/parameters/RunnableTypeQuery" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + responses: + "200": + description: Saved Inputs for a Runnable + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Input" + + /w/{workspace}/inputs/create: + post: + summary: Create an Input for future use in a script or flow + operationId: createInput + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableId" + - $ref: "#/components/parameters/RunnableTypeQuery" + requestBody: + description: Input + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateInput" + responses: + "201": + description: Input created + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/inputs/update: + post: + summary: Update an Input + operationId: updateInput + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: UpdateInput + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateInput" + responses: + "201": + description: Input updated + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/inputs/delete/{input}: + post: + summary: Delete a Saved Input + operationId: deleteInput + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/InputId" + responses: + "200": + description: Input deleted + content: + text/plain: + schema: + type: string + format: uuid + + /w/{workspace}/job_helpers/duckdb_connection_settings: + post: + summary: + Converts an S3 resource to the set of instructions necessary to connect + DuckDB to an S3 bucket + operationId: duckdbConnectionSettings + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: S3 resource to connect to + required: true + content: + application/json: + schema: + type: object + properties: + s3_resource: + $ref: "#/components/schemas/S3Resource" + responses: + "200": + description: Connection settings + content: + application/json: + schema: + type: object + properties: + connection_settings_str: + type: string + /w/{workspace}/job_helpers/v2/duckdb_connection_settings: + post: + summary: + Converts an S3 resource to the set of instructions necessary to connect + DuckDB to an S3 bucket + operationId: duckdbConnectionSettingsV2 + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: + S3 resource path to use to generate the connection settings. If empty, + the S3 resource defined in the workspace settings will be used + required: true + content: + application/json: + schema: + type: object + properties: + s3_resource_path: + type: string + responses: + "200": + description: Connection settings + content: + application/json: + schema: + type: object + properties: + connection_settings_str: + type: string + required: + - connection_settings_str + + /w/{workspace}/job_helpers/polars_connection_settings: + post: + summary: + Converts an S3 resource to the set of arguments necessary to connect + Polars to an S3 bucket + operationId: polarsConnectionSettings + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: S3 resource to connect to + required: true + content: + application/json: + schema: + type: object + properties: + s3_resource: + $ref: "#/components/schemas/S3Resource" + responses: + "200": + description: Connection settings + content: + application/json: + schema: + type: object + properties: + endpoint_url: + type: string + key: + type: string + secret: + type: string + use_ssl: + type: boolean + cache_regions: + type: boolean + client_kwargs: + $ref: "#/components/schemas/PolarsClientKwargs" + required: + - endpoint_url + - use_ssl + - cache_regions + - client_kwargs + /w/{workspace}/job_helpers/v2/polars_connection_settings: + post: + summary: + Converts an S3 resource to the set of arguments necessary to connect + Polars to an S3 bucket + operationId: polarsConnectionSettingsV2 + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: + S3 resource path to use to generate the connection settings. If empty, + the S3 resource defined in the workspace settings will be used + required: true + content: + application/json: + schema: + type: object + properties: + s3_resource_path: + type: string + responses: + "200": + description: Connection settings + content: + application/json: + schema: + type: object + properties: + s3fs_args: + type: object + properties: + endpoint_url: + type: string + key: + type: string + secret: + type: string + use_ssl: + type: boolean + cache_regions: + type: boolean + client_kwargs: + $ref: "#/components/schemas/PolarsClientKwargs" + required: + - endpoint_url + - use_ssl + - cache_regions + - client_kwargs + storage_options: + type: object + properties: + aws_endpoint_url: + type: string + aws_access_key_id: + type: string + aws_secret_access_key: + type: string + aws_region: + type: string + aws_allow_http: + type: string + required: + - aws_endpoint_url + - aws_region + - aws_allow_http + required: + - s3fs_args + - storage_options + /w/{workspace}/job_helpers/v2/s3_resource_info: + post: + summary: Returns the s3 resource associated to the provided path, or the + workspace default S3 resource + operationId: s3ResourceInfo + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: + S3 resource path to use. If empty, the S3 resource defined in the + workspace settings will be used + required: true + content: + application/json: + schema: + type: object + properties: + s3_resource_path: + type: string + responses: + "200": + description: Connection settings + content: + application/json: + schema: + $ref: "#/components/schemas/S3Resource" + + /w/{workspace}/job_helpers/test_connection: + get: + summary: Test connection to the workspace object storage + operationId: datasetStorageTestConnection + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: storage + in: query + schema: + type: string + responses: + "200": + description: Connection settings + content: + application/json: + schema: {} + + /w/{workspace}/job_helpers/list_stored_files: + get: + summary: List the file keys available in a workspace object storage + operationId: listStoredFiles + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: max_keys + in: query + required: true + schema: + type: integer + - name: marker + in: query + schema: + type: string + - name: prefix + in: query + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: List of file keys + content: + application/json: + schema: + type: object + properties: + next_marker: + type: string + windmill_large_files: + type: array + items: + $ref: "#/components/schemas/WindmillLargeFile" + restricted_access: + type: boolean + required: + - windmill_large_files + + /w/{workspace}/job_helpers/load_file_metadata: + get: + summary: Load metadata of the file + operationId: loadFileMetadata + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: file_key + in: query + required: true + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: FileMetadata + content: + application/json: + schema: + $ref: "#/components/schemas/WindmillFileMetadata" + + /w/{workspace}/job_helpers/load_file_preview: + get: + summary: Load a preview of the file + operationId: loadFilePreview + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: file_key + in: query + required: true + schema: + type: string + - name: file_size_in_bytes + in: query + schema: + type: integer + - name: file_mime_type + in: query + schema: + type: string + - name: csv_separator + in: query + schema: + type: string + - name: csv_has_header + in: query + schema: + type: boolean + - name: read_bytes_from + in: query + schema: + type: integer + - name: read_bytes_length + in: query + schema: + type: integer + - name: storage + in: query + schema: + type: string + responses: + "200": + description: FilePreview + content: + application/json: + schema: + $ref: "#/components/schemas/WindmillFilePreview" + + /w/{workspace}/job_helpers/load_parquet_preview/{path}: + get: + summary: Load a preview of a parquet file + operationId: loadParquetPreview + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: offset + in: query + schema: + type: number + - name: limit + in: query + schema: + type: number + - name: sort_col + in: query + schema: + type: string + - name: sort_desc + in: query + schema: + type: boolean + - name: search_col + in: query + schema: + type: string + - name: search_term + in: query + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: Parquet Preview + content: + application/json: + schema: {} + + /w/{workspace}/job_helpers/load_table_count/{path}: + get: + summary: Load the table row count + operationId: loadTableRowCount + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: search_col + in: query + schema: + type: string + - name: search_term + in: query + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: Table count + content: + application/json: + schema: + type: object + properties: + count: + type: number + + /w/{workspace}/job_helpers/load_csv_preview/{path}: + get: + summary: Load a preview of a csv file + operationId: loadCsvPreview + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: offset + in: query + schema: + type: number + - name: limit + in: query + schema: + type: number + - name: sort_col + in: query + schema: + type: string + - name: sort_desc + in: query + schema: + type: boolean + - name: search_col + in: query + schema: + type: string + - name: search_term + in: query + schema: + type: string + - name: storage + in: query + schema: + type: string + - name: csv_separator + in: query + schema: + type: string + responses: + "200": + description: Csv Preview + content: + application/json: + schema: {} + + /w/{workspace}/job_helpers/delete_s3_file: + delete: + summary: Permanently delete file from S3 + operationId: deleteS3File + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: file_key + in: query + required: true + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: Confirmation + content: + application/json: + schema: {} + + /w/{workspace}/job_helpers/move_s3_file: + get: + summary: Move a S3 file from one path to the other within the same bucket + operationId: moveS3File + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: src_file_key + in: query + required: true + schema: + type: string + - name: dest_file_key + in: query + required: true + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: Confirmation + content: + application/json: + schema: {} + + /w/{workspace}/job_helpers/upload_s3_file: + post: + summary: Upload file to S3 bucket + operationId: fileUpload + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: file_key + in: query + required: false + schema: + type: string + - name: file_extension + in: query + required: false + schema: + type: string + - name: s3_resource_path + in: query + required: false + schema: + type: string + - name: resource_type + in: query + required: false + schema: + type: string + - name: storage + in: query + schema: + type: string + - name: content_type + in: query + schema: + type: string + - name: content_disposition + in: query + schema: + type: string + requestBody: + description: File content + required: true + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + "200": + description: File upload status + content: + application/json: + schema: + type: object + properties: + file_key: + type: string + required: + - file_key + + /w/{workspace}/job_helpers/download_s3_file: + get: + summary: Download file to S3 bucket + operationId: fileDownload + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: file_key + in: query + required: true + schema: + type: string + - name: s3_resource_path + in: query + required: false + schema: + type: string + - name: resource_type + in: query + required: false + schema: + type: string + - name: storage + in: query + schema: + type: string + responses: + "200": + description: Chunk of the downloaded file + content: + application/octet-stream: + schema: + type: string + format: binary + + /w/{workspace}/job_helpers/download_s3_parquet_file_as_csv: + get: + summary: Download file to S3 bucket + operationId: fileDownloadParquetAsCsv + tags: + - helpers + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: file_key + in: query + required: true + schema: + type: string + - name: s3_resource_path + in: query + required: false + schema: + type: string + - name: resource_type + in: query + required: false + schema: + type: string + responses: + "200": + description: The downloaded file + content: + text/csv: + schema: + type: string + + /w/{workspace}/job_metrics/get/{id}: + post: + summary: get job metrics + operationId: getJobMetrics + tags: + - metrics + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + requestBody: + description: parameters for statistics retrieval + required: true + content: + application/json: + schema: + type: object + properties: + timeseries_max_datapoints: + type: integer + from_timestamp: + type: string + format: date-time + to_timestamp: + type: string + format: date-time + + responses: + "200": + description: job details + content: + application/json: + schema: + type: object + properties: + metrics_metadata: + type: array + items: + $ref: "#/components/schemas/MetricMetadata" + scalar_metrics: + type: array + items: + $ref: "#/components/schemas/ScalarMetric" + timeseries_metrics: + type: array + items: + $ref: "#/components/schemas/TimeseriesMetric" + + /w/{workspace}/job_metrics/set_progress/{id}: + post: + summary: set job metrics + operationId: setJobProgress + tags: + - metrics + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + requestBody: + description: parameters for statistics retrieval + required: true + content: + application/json: + schema: + type: object + properties: + percent: + type: integer + flow_job_id: + type: string + format: uuid + + responses: + "200": + description: Job progress updated + content: + application/json: + schema: {} + + /w/{workspace}/job_metrics/get_progress/{id}: + get: + summary: get job progress + operationId: getJobProgress + tags: + - metrics + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: job progress between 0 and 99 + content: + application/json: + schema: + type: integer + /service_logs/list_files: + get: + summary: list log files ordered by timestamp + operationId: listLogFiles + tags: + - service_logs + parameters: + - $ref: "#/components/parameters/Before" + - $ref: "#/components/parameters/After" + - name: with_error + in: query + required: false + schema: + type: boolean + responses: + "200": + description: time + content: + application/json: + schema: + type: array + items: + type: object + properties: + hostname: + type: string + mode: + type: string + worker_group: + type: string + log_ts: + type: string + format: date-time + file_path: + type: string + ok_lines: + type: integer + err_lines: + type: integer + json_fmt: + type: boolean + required: + - hostname + - mode + - log_ts + - file_path + - json_fmt + + /service_logs/get_log_file/{path}: + get: + summary: get log file by path + operationId: getLogFile + tags: + - service_logs + parameters: + - $ref: "#/components/parameters/Path" + responses: + "200": + description: log stream + content: + text/plain: + schema: + type: string + + /concurrency_groups/list: + get: + summary: List all concurrency groups + operationId: listConcurrencyGroups + tags: + - concurrencyGroups + responses: + "200": + description: all concurrency groups + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ConcurrencyGroup" + /concurrency_groups/prune/{concurrency_id}: + delete: + summary: Delete concurrency group + operationId: deleteConcurrencyGroup + tags: + - concurrencyGroups + parameters: + - $ref: "#/components/parameters/ConcurrencyId" + responses: + "200": + description: concurrency group removed + content: + application/json: + schema: + type: object + properties: {} + /concurrency_groups/{id}/key: + get: + summary: Get the concurrency key for a job that has concurrency limits enabled + operationId: getConcurrencyKey + tags: + - concurrencyGroups + parameters: + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: concurrency key for given job + content: + application/json: + schema: + type: string + /w/{workspace}/concurrency_groups/list_jobs: + get: + summary: Get intervals of job runtime concurrency + operationId: listExtendedJobs + tags: + - concurrencyGroups + - job + parameters: + - name: concurrency_key + in: query + required: false + schema: + type: string + - name: row_limit + in: query + required: false + schema: + type: number + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/CreatedBy" + - $ref: "#/components/parameters/Label" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/ScriptExactPath" + - $ref: "#/components/parameters/ScriptStartPath" + - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/ScriptExactHash" + - $ref: "#/components/parameters/StartedBefore" + - $ref: "#/components/parameters/StartedAfter" + - $ref: "#/components/parameters/CreatedOrStartedBefore" + - $ref: "#/components/parameters/Running" + - $ref: "#/components/parameters/ScheduledForBeforeNow" + - $ref: "#/components/parameters/CreatedOrStartedAfter" + - $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob" + - $ref: "#/components/parameters/JobKinds" + - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/Tag" + - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: is_skipped + description: is the job skipped + in: query + schema: + type: boolean + - name: is_flow_step + description: is the job a flow step + in: query + schema: + type: boolean + - name: has_null_parent + description: has null parent + in: query + schema: + type: boolean + - name: success + description: filter on successful jobs + in: query + schema: + type: boolean + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean + - name: is_not_schedule + description: is not a scheduled job + in: query + schema: + type: boolean + responses: + "200": + description: time + content: + application/json: + schema: + $ref: "#/components/schemas/ExtendedJobs" + + /srch/w/{workspace}/index/search/job: + get: + summary: Search through jobs with a string query + operationId: searchJobsIndex + tags: + - indexSearch + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: search_query + in: query + required: true + schema: + type: string + responses: + "200": + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: a list of the terms that couldn't be parsed (and thus ignored) + type: array + items: + type: object + properties: + dancer: + type: string + hits: + description: the jobs that matched the query + type: array + items: + $ref: "#/components/schemas/JobSearchHit" + + /srch/index/search/service_logs: + get: + summary: Search through service logs with a string query + operationId: searchLogsIndex + tags: + - indexSearch + parameters: + - name: search_query + in: query + required: true + schema: + type: string + - name: mode + in: query + required: true + schema: + type: string + - name: worker_group + in: query + required: false + schema: + type: string + - name: hostname + in: query + required: true + schema: + type: string + - name: min_ts + in: query + required: false + schema: + type: string + format: date-time + - name: max_ts + in: query + required: false + schema: + type: string + format: date-time + responses: + "200": + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: a list of the terms that couldn't be parsed (and thus ignored) + type: array + items: + type: string + hits: + description: log files that matched the query + type: array + items: + $ref: "#/components/schemas/LogSearchHit" + + /srch/index/search/count_service_logs: + get: + summary: Search and count the log line hits on every provided host + operationId: countSearchLogsIndex + tags: + - indexSearch + parameters: + - name: search_query + in: query + required: true + schema: + type: string + - name: hosts + in: query + required: true + schema: + type: string + - name: min_ts + in: query + required: false + schema: + type: string + format: date-time + - name: max_ts + in: query + required: false + schema: + type: string + format: date-time + responses: + "200": + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: a list of the terms that couldn't be parsed (and thus ignored) + type: array + items: + type: string + count_per_host: + description: count of log lines that matched the query per hostname + type: object + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + cookieAuth: + type: apiKey + in: cookie + name: token + + parameters: + Key: + name: key + in: path + required: true + schema: + type: string + WorkspaceId: + name: workspace + in: path + required: true + schema: + type: string + VersionId: + name: version + in: path + required: true + schema: + type: number + Token: + name: token + in: path + required: true + schema: + type: string + AccountId: + name: id + in: path + required: true + schema: + type: integer + ClientName: + name: client_name + in: path + required: true + schema: + type: string + ScriptPath: + name: path + in: path + required: true + schema: + type: string + ScriptHash: + name: hash + in: path + required: true + schema: + type: string + JobId: + name: id + in: path + required: true + schema: + type: string + format: uuid + Path: + name: path + in: path + required: true + schema: + type: string + PathId: + name: id + in: path + required: true + schema: + type: integer + PathVersion: + name: version + in: path + required: true + schema: + type: integer + Name: + name: name + in: path + required: true + schema: + type: string + Page: + name: page + description: which page to return (start at 1, default 1) + in: query + schema: + type: integer + PerPage: + name: per_page + description: number of items to return for a given page (default 30, max 100) + in: query + schema: + type: integer + OrderDesc: + name: order_desc + description: order by desc order (default true) + in: query + schema: + type: boolean + CreatedBy: + name: created_by + description: mask to filter exact matching user creator + in: query + schema: + type: string + Label: + name: label + description: mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') + in: query + schema: + type: string + ParentJob: + name: parent_job + description: + The parent job that is at the origin and responsible for the execution + of this script if any + in: query + schema: + type: string + format: uuid + WorkerTag: + name: tag + description: Override the tag to use + in: query + schema: + type: string + CacheTtl: + name: cache_ttl + description: Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl + in: query + schema: + type: string + NewJobId: + name: job_id + description: + The job id to assign to the created job. if missing, job is chosen + randomly using the ULID scheme. If a job id already exists in the queue + or as a completed job, the request to create one will fail (Bad Request) + in: query + schema: + type: string + format: uuid + IncludeHeader: + name: include_header + description: | + List of headers's keys (separated with ',') whove value are added to the args + Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + in: query + schema: + type: string + QueueLimit: + name: queue_limit + description: | + The maximum size of the queue for which the request would get rejected if that job would push it above that limit + in: query + schema: + type: string + Payload: + name: payload + description: | + The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent + `encodeURIComponent(btoa(JSON.stringify({a: 2})))` + in: query + schema: + type: string + ScriptStartPath: + name: script_path_start + description: mask to filter matching starting path + in: query + schema: + type: string + SchedulePath: + name: schedule_path + description: mask to filter by schedule path + in: query + schema: + type: string + ScriptExactPath: + name: script_path_exact + description: mask to filter exact matching path + in: query + schema: + type: string + ScriptExactHash: + name: script_hash + description: mask to filter exact matching path + in: query + schema: + type: string + CreatedBefore: + name: created_before + description: filter on created before (inclusive) timestamp + in: query + schema: + type: string + format: date-time + CreatedAfter: + name: created_after + description: filter on created after (exclusive) timestamp + in: query + schema: + type: string + format: date-time + StartedBefore: + name: started_before + description: filter on started before (inclusive) timestamp + in: query + schema: + type: string + format: date-time + StartedAfter: + name: started_after + description: filter on started after (exclusive) timestamp + in: query + schema: + type: string + format: date-time + Before: + name: before + description: filter on started before (inclusive) timestamp + in: query + schema: + type: string + format: date-time + CreatedOrStartedAfter: + name: created_or_started_after + description: + filter on created_at for non non started job and started_at otherwise + after (exclusive) timestamp + in: query + schema: + type: string + format: date-time + CreatedOrStartedAfterCompletedJob: + name: created_or_started_after_completed_jobs + description: + filter on created_at for non non started job and started_at otherwise + after (exclusive) timestamp but only for the completed jobs + in: query + schema: + type: string + format: date-time + CreatedOrStartedBefore: + name: created_or_started_before + description: + filter on created_at for non non started job and started_at otherwise + before (inclusive) timestamp + in: query + schema: + type: string + format: date-time + Success: + name: success + description: filter on successful jobs + in: query + schema: + type: boolean + ScheduledForBeforeNow: + name: scheduled_for_before_now + description: filter on jobs scheduled_for before now (hence waitinf for a worker) + in: query + schema: + type: boolean + Suspended: + name: suspended + description: filter on suspended jobs + in: query + schema: + type: boolean + Running: + name: running + description: filter on running jobs + in: query + schema: + type: boolean + ArgsFilter: + name: args + description: filter on jobs containing those args as a json subset (@> in postgres) + in: query + schema: + type: string + Tag: + name: tag + description: filter on jobs with a given tag/worker group + in: query + schema: + type: string + ResultFilter: + name: result + description: filter on jobs containing those result as a json subset (@> in postgres) + in: query + schema: + type: string + After: + name: after + description: filter on created after (exclusive) timestamp + in: query + schema: + type: string + format: date-time + Username: + name: username + description: filter on exact username of user + in: query + schema: + type: string + Operation: + name: operation + description: filter on exact or prefix name of operation + in: query + schema: + type: string + ResourceName: + name: resource + description: filter on exact or prefix name of resource + in: query + schema: + type: string + ActionKind: + name: action_kind + description: filter on type of operation + in: query + schema: + type: string + enum: [Create, Update, Delete, Execute] + JobKinds: + name: job_kinds + description: + filter on job kind (values 'preview', 'script', 'dependencies', 'flow') + separated by, + in: query + schema: + type: string + # correct type is below but explode not supported by our codegen + # type: array + # items: + # type: string + # enum: ["preview", "script", "dependencies"] + # explode: false + RunnableId: + name: runnable_id + in: query + schema: + type: string + RunnableTypeQuery: + name: runnable_type + in: query + schema: + $ref: "#/components/schemas/RunnableType" + InputId: + name: input + in: path + required: true + schema: + type: string + GetStarted: + name: get_started + in: query + schema: + type: boolean + ConcurrencyId: + name: concurrency_id + in: path + required: true + schema: + type: string + + schemas: + $ref: "../../openflow.openapi.yaml#/components/schemas" + + AiResource: + type: object + properties: + path: + type: string + provider: + type: string + required: + - path + - provider + Script: + type: object + properties: + workspace_id: + type: string + hash: + type: string + path: + type: string + parent_hashes: + type: array + description: | + The first element is the direct parent of the script, the second is the parent of the first, etc + items: + type: string + summary: + type: string + description: + type: string + content: + type: string + created_by: + type: string + created_at: + type: string + format: date-time + archived: + type: boolean + schema: + type: object + deleted: + type: boolean + is_template: + type: boolean + extra_perms: + type: object + additionalProperties: + type: boolean + lock: + type: string + lock_error_logs: + type: string + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + php, + rust, + ansible, + ] + kind: + type: string + enum: [script, failure, trigger, command, approval] + starred: + type: boolean + tag: + type: string + has_draft: + type: boolean + draft_only: + type: boolean + envs: + type: array + items: + type: string + concurrent_limit: + type: integer + concurrency_time_window_s: + type: integer + concurrency_key: + type: string + cache_ttl: + type: number + dedicated_worker: + type: boolean + ws_error_handler_muted: + type: boolean + priority: + type: integer + restart_unless_cancelled: + type: boolean + timeout: + type: integer + delete_after_use: + type: boolean + visible_to_runner_only: + type: boolean + no_main_func: + type: boolean + codebase: + type: string + has_preprocessor: + type: boolean + + required: + - hash + - path + - summary + - description + - content + - created_by + - created_at + - archived + - deleted + - is_template + - extra_perms + - language + - kind + - starred + - no_main_func + - has_preprocessor + + + NewScript: + type: object + properties: + path: + type: string + parent_hash: + type: string + summary: + type: string + description: + type: string + content: + type: string + schema: + type: object + is_template: + type: boolean + lock: + type: string + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + php, + rust, + ansible, + ] + kind: + type: string + enum: [script, failure, trigger, command, approval] + tag: + type: string + draft_only: + type: boolean + envs: + type: array + items: + type: string + concurrent_limit: + type: integer + concurrency_time_window_s: + type: integer + cache_ttl: + type: number + dedicated_worker: + type: boolean + ws_error_handler_muted: + type: boolean + priority: + type: integer + restart_unless_cancelled: + type: boolean + timeout: + type: integer + delete_after_use: + type: boolean + deployment_message: + type: string + concurrency_key: + type: string + visible_to_runner_only: + type: boolean + no_main_func: + type: boolean + codebase: + type: string + has_preprocessor: + type: boolean + required: + - path + - summary + - description + - content + - language + + NewScriptWithDraft: + allOf: + - $ref: "#/components/schemas/NewScript" + - type: object + properties: + draft: + $ref: "#/components/schemas/NewScript" + hash: + type: string + required: + - hash + + ScriptHistory: + type: object + properties: + script_hash: + type: string + deployment_msg: + type: string + required: + - script_hash + + ScriptArgs: + type: object + additionalProperties: {} + + Input: + type: object + properties: + id: + type: string + name: + type: string + created_by: + type: string + created_at: + type: string + format: date-time + is_public: + type: boolean + success: + type: boolean + required: + - id + - name + - args + - created_by + - created_at + - is_public + + CreateInput: + type: object + properties: + name: + type: string + args: + type: object + required: + - name + - args + - created_by + + UpdateInput: + type: object + properties: + id: + type: string + name: + type: string + is_public: + type: boolean + required: + - id + - name + - is_public + + RunnableType: + type: string + enum: ["ScriptHash", "ScriptPath", "FlowPath"] + + QueuedJob: + type: object + properties: + workspace_id: + type: string + id: + type: string + format: uuid + parent_job: + type: string + format: uuid + created_by: + type: string + created_at: + type: string + format: date-time + started_at: + type: string + format: date-time + scheduled_for: + type: string + format: date-time + running: + type: boolean + script_path: + type: string + script_hash: + type: string + args: + $ref: "#/components/schemas/ScriptArgs" + logs: + type: string + raw_code: + type: string + canceled: + type: boolean + canceled_by: + type: string + canceled_reason: + type: string + last_ping: + type: string + format: date-time + job_kind: + type: string + enum: + [ + "script", + "preview", + "dependencies", + "flowdependencies", + "appdependencies", + "flow", + "flowpreview", + "script_hub", + "identity", + "deploymentcallback", + "singlescriptflow", + ] + schedule_path: + type: string + permissioned_as: + type: string + description: | + The user (u/userfoo) or group (g/groupfoo) whom + the execution of this script will be permissioned_as and by extension its DT_TOKEN. + flow_status: + $ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus" + raw_flow: + $ref: "../../openflow.openapi.yaml#/components/schemas/FlowValue" + is_flow_step: + type: boolean + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + php, + rust, + ansible, + ] + email: + type: string + visible_to_owner: + type: boolean + mem_peak: + type: integer + tag: + type: string + priority: + type: integer + self_wait_time_ms: + type: number + aggregate_wait_time_ms: + type: number + suspend: + type: number + required: + - id + - running + - canceled + - job_kind + - permissioned_as + - is_flow_step + - email + - visible_to_owner + - tag + + CompletedJob: + type: object + properties: + workspace_id: + type: string + id: + type: string + format: uuid + parent_job: + type: string + format: uuid + created_by: + type: string + created_at: + type: string + format: date-time + started_at: + type: string + format: date-time + duration_ms: + type: integer + success: + type: boolean + script_path: + type: string + script_hash: + type: string + args: + $ref: "#/components/schemas/ScriptArgs" + result: {} + logs: + type: string + deleted: + type: boolean + raw_code: + type: string + canceled: + type: boolean + canceled_by: + type: string + canceled_reason: + type: string + job_kind: + type: string + enum: + [ + "script", + "preview", + "dependencies", + "flow", + "flowdependencies", + "appdependencies", + "flowpreview", + "script_hub", + "identity", + "deploymentcallback", + "singlescriptflow", + ] + schedule_path: + type: string + permissioned_as: + type: string + description: | + The user (u/userfoo) or group (g/groupfoo) whom + the execution of this script will be permissioned_as and by extension its DT_TOKEN. + flow_status: + $ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus" + raw_flow: + $ref: "../../openflow.openapi.yaml#/components/schemas/FlowValue" + is_flow_step: + type: boolean + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + php, + rust, + ansible, + ] + is_skipped: + type: boolean + email: + type: string + visible_to_owner: + type: boolean + mem_peak: + type: integer + tag: + type: string + priority: + type: integer + labels: + type: array + items: + type: string + self_wait_time_ms: + type: number + aggregate_wait_time_ms: + type: number + required: + - id + - created_by + - duration_ms + - created_at + - started_at + - success + - canceled + - job_kind + - permissioned_as + - is_flow_step + - is_skipped + - email + - visible_to_owner + - tag + + ObscuredJob: + type: object + properties: + typ: + type: string + started_at: + type: string + format: date-time + duration_ms: + type: number + Job: + oneOf: + - allOf: + - $ref: "#/components/schemas/CompletedJob" + - type: object + properties: + type: + type: string + enum: [CompletedJob] + - allOf: + - $ref: "#/components/schemas/QueuedJob" + - type: object + properties: + type: + type: string + enum: [QueuedJob] + discriminator: + propertyName: type + + User: + type: object + properties: + email: + type: string + username: + type: string + is_admin: + type: boolean + is_super_admin: + type: boolean + created_at: + type: string + format: date-time + operator: + type: boolean + disabled: + type: boolean + groups: + type: array + items: + type: string + folders: + type: array + items: + type: string + folders_owners: + type: array + items: + type: string + required: + - email + - username + - is_admin + - is_super_admin + - created_at + - operator + - disabled + - folders + - folders_owners + + UserUsage: + type: object + properties: + email: + type: string + executions: + type: number + + Login: + type: object + properties: + email: + type: string + password: + type: string + required: + - email + - password + + EditWorkspaceUser: + type: object + properties: + is_admin: + type: boolean + operator: + type: boolean + disabled: + type: boolean + + TruncatedToken: + type: object + properties: + label: + type: string + expiration: + type: string + format: date-time + token_prefix: + type: string + created_at: + type: string + format: date-time + last_used_at: + type: string + format: date-time + scopes: + type: array + items: + type: string + email: + type: string + required: + - token_prefix + - created_at + - last_used_at + + NewToken: + type: object + properties: + label: + type: string + expiration: + type: string + format: date-time + scopes: + type: array + items: + type: string + workspace_id: + type: string + + NewTokenImpersonate: + type: object + properties: + label: + type: string + expiration: + type: string + format: date-time + impersonate_email: + type: string + workspace_id: + type: string + required: + - impersonate_email + + ListableVariable: + type: object + properties: + workspace_id: + type: string + path: + type: string + value: + type: string + is_secret: + type: boolean + description: + type: string + account: + type: integer + is_oauth: + type: boolean + extra_perms: + type: object + additionalProperties: + type: boolean + is_expired: + type: boolean + refresh_error: + type: string + is_linked: + type: boolean + is_refreshed: + type: boolean + expires_at: + type: string + format: date-time + required: + - workspace_id + - path + - is_secret + - extra_perms + + ContextualVariable: + type: object + properties: + name: + type: string + value: + type: string + description: + type: string + is_custom: + type: boolean + required: + - name + - value + - description + - is_custom + + CreateVariable: + type: object + properties: + path: + type: string + value: + type: string + is_secret: + type: boolean + description: + type: string + account: + type: integer + is_oauth: + type: boolean + expires_at: + type: string + format: date-time + required: + - path + - value + - is_secret + - description + + EditVariable: + type: object + properties: + path: + type: string + value: + type: string + is_secret: + type: boolean + description: + type: string + + AuditLog: + type: object + properties: + id: + type: integer + timestamp: + type: string + format: date-time + username: + type: string + operation: + type: string + enum: + - "jobs.run" + - "jobs.run.script" + - "jobs.run.preview" + - "jobs.run.flow" + - "jobs.run.flow_preview" + - "jobs.run.script_hub" + - "jobs.run.dependencies" + - "jobs.run.identity" + - "jobs.run.noop" + - "jobs.flow_dependencies" + - "jobs" + - "jobs.cancel" + - "jobs.force_cancel" + - "jobs.disapproval" + - "jobs.delete" + - "account.delete" + - "ai.request" + - "resources.create" + - "resources.update" + - "resources.delete" + - "resource_types.create" + - "resource_types.update" + - "resource_types.delete" + - "schedule.create" + - "schedule.setenabled" + - "schedule.edit" + - "schedule.delete" + - "scripts.create" + - "scripts.update" + - "scripts.archive" + - "scripts.delete" + - "users.create" + - "users.delete" + - "users.update" + - "users.login" + - "users.login_failure" + - "users.logout" + - "users.accept_invite" + - "users.decline_invite" + - "users.token.create" + - "users.token.delete" + - "users.add_to_workspace" + - "users.add_global" + - "users.setpassword" + - "users.impersonate" + - "users.leave_workspace" + - "oauth.login" + - "oauth.login_failure" + - "oauth.signup" + - "variables.create" + - "variables.delete" + - "variables.update" + - "flows.create" + - "flows.update" + - "flows.delete" + - "flows.archive" + - "apps.create" + - "apps.update" + - "apps.delete" + - "folder.create" + - "folder.update" + - "folder.delete" + - "folder.add_owner" + - "folder.remove_owner" + - "group.create" + - "group.delete" + - "group.edit" + - "group.adduser" + - "group.removeuser" + - "igroup.create" + - "igroup.delete" + - "igroup.adduser" + - "igroup.removeuser" + - "variables.decrypt_secret" + - "workspaces.edit_command_script" + - "workspaces.edit_deploy_to" + - "workspaces.edit_auto_invite_domain" + - "workspaces.edit_webhook" + - "workspaces.edit_copilot_config" + - "workspaces.edit_error_handler" + - "workspaces.create" + - "workspaces.update" + - "workspaces.archive" + - "workspaces.unarchive" + - "workspaces.delete" + action_kind: + type: string + enum: ["Created", "Updated", "Delete", "Execute"] + resource: + type: string + parameters: + type: object + required: + - id + - timestamp + - username + - operation + - action_kind + + MainArgSignature: + type: object + properties: + type: + type: string + enum: ["Valid", "Invalid"] + error: + type: string + star_args: + type: boolean + star_kwargs: + type: boolean + args: + type: array + items: + type: object + properties: + name: + type: string + typ: + oneOf: + - type: string + enum: + [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ] + - type: object + properties: + resource: + type: string + nullable: true + required: + - resource + - type: object + properties: + str: + type: array + items: + type: string + nullable: true + required: + - str + - type: object + properties: + object: + type: array + items: + type: object + properties: + key: + type: string + typ: + oneOf: + - type: string + enum: + [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ] + - type: object + properties: + str: {} + required: [str] + required: + - key + - typ + required: + - object + - type: object + properties: + list: + oneOf: + - type: string + enum: + [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ] + - type: object + properties: + str: {} + required: [str] + nullable: true + required: + - list + has_default: + type: boolean + default: {} + required: + - name + - typ + no_main_func: + type: boolean + nullable: true + has_preprocessor: + type: boolean + nullable: true + required: + - star_args + - start_kwargs + - args + - type + - error + - no_main_func + - has_preprocessor + + Preview: + type: object + properties: + content: + type: string + path: + type: string + args: + $ref: "#/components/schemas/ScriptArgs" + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + php, + rust, + ansible, + ] + tag: + type: string + kind: + type: string + enum: [code, identity, http] + dedicated_worker: + type: boolean + lock: + type: string + required: + - args + + WorkflowTask: + type: object + properties: + args: + $ref: "#/components/schemas/ScriptArgs" + required: + - args + + WorkflowStatusRecord: + type: object + additionalProperties: + $ref: "#/components/schemas/WorkflowStatus" + + WorkflowStatus: + type: object + properties: + scheduled_for: + type: string + format: date-time + started_at: + type: string + format: date-time + duration_ms: + type: number + name: + type: string + + CreateResource: + type: object + properties: + path: + type: string + value: {} + description: + type: string + resource_type: + type: string + required: + - path + - value + - resource_type + + EditResource: + type: object + properties: + path: + type: string + description: + type: string + value: {} + + Resource: + type: object + properties: + workspace_id: + type: string + path: + type: string + description: + type: string + resource_type: + type: string + value: {} + is_oauth: + type: boolean + extra_perms: + type: object + additionalProperties: + type: boolean + created_by: + type: string + edited_at: + type: string + format: date-time + required: + - path + - resource_type + - is_oauth + + ListableResource: + type: object + properties: + workspace_id: + type: string + path: + type: string + description: + type: string + resource_type: + type: string + value: {} + is_oauth: + type: boolean + extra_perms: + type: object + additionalProperties: + type: boolean + is_expired: + type: boolean + refresh_error: + type: string + is_linked: + type: boolean + is_refreshed: + type: boolean + account: + type: number + created_by: + type: string + edited_at: + type: string + format: date-time + required: + - path + - resource_type + - is_oauth + - is_linked + - is_refreshed + + ResourceType: + type: object + properties: + workspace_id: + type: string + name: + type: string + schema: {} + description: + type: string + created_by: + type: string + edited_at: + type: string + format: date-time + format_extension: + type: string + required: + - name + + EditResourceType: + type: object + properties: + schema: {} + description: + type: string + + Schedule: + type: object + properties: + path: + type: string + edited_by: + type: string + edited_at: + type: string + format: date-time + schedule: + type: string + timezone: + type: string + enabled: + type: boolean + script_path: + type: string + is_flow: + type: boolean + args: + $ref: "#/components/schemas/ScriptArgs" + extra_perms: + type: object + additionalProperties: + type: boolean + email: + type: string + error: + type: string + on_failure: + # a reference to a script path, flow path, or webhook (script/, flow/) + type: string + on_failure_times: + type: number + on_failure_exact: + type: boolean + on_failure_extra_args: + $ref: "#/components/schemas/ScriptArgs" + on_recovery: + type: string + on_recovery_times: + type: number + on_recovery_extra_args: + $ref: "#/components/schemas/ScriptArgs" + on_success: + type: string + on_success_extra_args: + $ref: "#/components/schemas/ScriptArgs" + ws_error_handler_muted: + type: boolean + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" + summary: + type: string + no_flow_overlap: + type: boolean + tag: + type: string + paused_until: + type: string + format: date-time + required: + - path + - edited_by + - edited_at + - schedule + - script_path + - timezone + - extra_perms + - is_flow + - enabled + - email + + ScheduleWJobs: + allOf: + - $ref: "#/components/schemas/Schedule" + - type: object + properties: + jobs: + type: array + items: + type: object + properties: + id: + type: string + success: + type: boolean + duration_ms: + type: number + required: + - id + - success + - duration_ms + + NewSchedule: + type: object + properties: + path: + type: string + schedule: + type: string + timezone: + type: string + script_path: + type: string + is_flow: + type: boolean + args: + $ref: "#/components/schemas/ScriptArgs" + enabled: + type: boolean + on_failure: + # a reference to a script path, flow path, or webhook (script/, flow/) + type: string + on_failure_times: + type: number + on_failure_exact: + type: boolean + on_failure_extra_args: + $ref: "#/components/schemas/ScriptArgs" + on_recovery: + type: string + on_recovery_times: + type: number + on_recovery_extra_args: + $ref: "#/components/schemas/ScriptArgs" + on_success: + type: string + on_success_extra_args: + $ref: "#/components/schemas/ScriptArgs" + ws_error_handler_muted: + type: boolean + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" + no_flow_overlap: + type: boolean + summary: + type: string + tag: + type: string + paused_until: + type: string + format: date-time + required: + - path + - schedule + - timezone + - script_path + - is_flow + - args + + EditSchedule: + type: object + properties: + schedule: + type: string + timezone: + type: string + args: + $ref: "#/components/schemas/ScriptArgs" + on_failure: + # a reference to a script path, flow path, or webhook (script/, flow/) + type: string + on_failure_times: + type: number + on_failure_exact: + type: boolean + on_failure_extra_args: + $ref: "#/components/schemas/ScriptArgs" + on_recovery: + type: string + on_recovery_times: + type: number + on_recovery_extra_args: + $ref: "#/components/schemas/ScriptArgs" + on_success: + type: string + on_success_extra_args: + $ref: "#/components/schemas/ScriptArgs" + ws_error_handler_muted: + type: boolean + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" + no_flow_overlap: + type: boolean + summary: + type: string + tag: + type: string + paused_until: + type: string + format: date-time + required: + - schedule + - timezone + - script_path + - is_flow + - args + + TriggersExtraProperty: + type: object + properties: + email: + type: string + extra_perms: + type: object + additionalProperties: + type: boolean + workspace_id: + type: string + + HttpTrigger: + allOf: + - $ref: '#/components/schemas/TriggersExtraProperty' + type: object + properties: + path: + type: string + edited_by: + type: string + edited_at: + type: string + format: date-time + script_path: + type: string + route_path: + type: string + static_asset_config: + type: object + properties: + s3: + type: string + storage: + type: string + filename: + type: string + required: + - s3 + is_flow: + type: boolean + http_method: + type: string + enum: + - get + - post + - put + - delete + - patch + is_async: + type: boolean + requires_auth: + type: boolean + + required: + - path + - edited_by + - edited_at + - script_path + - route_path + - extra_perms + - is_flow + - email + - workspace_id + - is_async + - requires_auth + - http_method + + NewHttpTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + route_path: + type: string + static_asset_config: + type: object + properties: + s3: + type: string + storage: + type: string + filename: + type: string + required: + - s3 + is_flow: + type: boolean + http_method: + type: string + enum: + - get + - post + - put + - delete + - patch + is_async: + type: boolean + requires_auth: + type: boolean + + required: + - path + - script_path + - route_path + - is_flow + - is_async + - requires_auth + - http_method + + EditHttpTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + route_path: + type: string + static_asset_config: + type: object + properties: + s3: + type: string + storage: + type: string + filename: + type: string + required: + - s3 + is_flow: + type: boolean + http_method: + type: string + enum: + - get + - post + - put + - delete + - patch + is_async: + type: boolean + requires_auth: + type: boolean + required: + - path + - script_path + - is_flow + - kind + - is_async + - requires_auth + - http_method + + TriggersCount: + type: object + properties: + primary_schedule: + type: object + properties: + schedule: + type: string + schedule_count: + type: number + http_routes_count: + type: number + webhook_count: + type: number + email_count: + type: number + websocket_count: + type: number + database_count: + type: number + + WebsocketTrigger: + allOf: + - $ref: '#/components/schemas/TriggersExtraProperty' + type: object + properties: + path: + type: string + edited_by: + type: string + edited_at: + type: string + format: date-time + script_path: + type: string + url: + type: string + is_flow: + type: boolean + server_id: + type: string + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + $ref: "#/components/schemas/WebsocketTriggerInitialMessage" + url_runnable_args: + $ref: "#/components/schemas/ScriptArgs" + + required: + - path + - edited_by + - edited_at + - script_path + - url + - extra_perms + - is_flow + - email + - workspace_id + - enabled + - filters + - initial_messages + - url_runnable_args + + NewWebsocketTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + url: + type: string + enabled: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + $ref: "#/components/schemas/WebsocketTriggerInitialMessage" + url_runnable_args: + $ref: "#/components/schemas/ScriptArgs" + + required: + - path + - script_path + - url + - is_flow + - filters + - initial_messages + - url_runnable_args + + EditWebsocketTrigger: + type: object + properties: + url: + type: string + path: + type: string + script_path: + type: string + is_flow: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + $ref: "#/components/schemas/WebsocketTriggerInitialMessage" + url_runnable_args: + $ref: "#/components/schemas/ScriptArgs" + + required: + - path + - script_path + - url + - is_flow + - filters + - initial_messages + - url_runnable_args + + WebsocketTriggerInitialMessage: + anyOf: + - type: object + properties: + raw_message: + type: string + required: + - raw_message + - type: object + properties: + runnable_result: + type: object + properties: + path: + type: string + args: + $ref: "#/components/schemas/ScriptArgs" + is_flow: + type: boolean + required: + - path + - args + - is_flow + required: + - runnable_result + + TableToTrack: + type: object + properties: + table_name: + type: string + columns_name: + type: array + items: + type: string + required: + - table_name + + DatabaseToTrack: + type: object + properties: + username: + type: string + password: + type: string + host: + type: string + port: + type: number + required: + - username + - host + - port + + DatabaseToTrackWithoutPass: + allOf: + $ref: "#/components/schemas/DatabaseToTrack" + type: object + properties: + username: + type: string + host: + type: string + port: + type: number + required: + - username + - host + - port + + DatabaseTrigger: + allOf: + - $ref: '#/components/schemas/TriggersExtraProperty' + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + database: + $ref: "#/components/schemas/DatabaseToTrackWithoutPass" + required: + - username + - host + - port + table_to_track: + type: array + items: + $ref: "#/components/schemas/TableToTrack" + + required: + - path + - script_path + - is_flow + - enabled + - database + + NewDatabaseTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + database: + $ref: "#/components/schemas/DatabaseToTrack" + required: + - username + - host + - port + table_to_track: + type: array + items: + $ref: "#/components/schemas/TableToTrack" + + required: + - path + - script_path + - is_flow + - enabled + - database + + EditDatabaseTrigger: + type: object + properties: + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + database: + $ref: "#/components/schemas/DatabaseToTrack" + required: + - username + - host + - port + table_to_track: + type: array + items: + $ref: "#/components/schemas/TableToTrack" + + Group: + type: object + properties: + name: + type: string + summary: + type: string + members: + type: array + items: + type: string + extra_perms: + type: object + additionalProperties: + type: boolean + required: + - name + + InstanceGroup: + type: object + properties: + name: + type: string + summary: + type: string + emails: + type: array + items: + type: string + required: + - name + + Folder: + type: object + properties: + name: + type: string + owners: + type: array + items: + type: string + extra_perms: + type: object + additionalProperties: + type: boolean + summary: + type: string + created_by: + type: string + edited_at: + type: string + format: date-time + required: + - name + - owners + - extra_perms + + WorkerPing: + type: object + properties: + worker: + type: string + worker_instance: + type: string + last_ping: + type: number + started_at: + type: string + format: date-time + ip: + type: string + jobs_executed: + type: integer + custom_tags: + type: array + items: + type: string + worker_group: + type: string + wm_version: + type: string + last_job_id: + type: string + last_job_workspace_id: + type: string + occupancy_rate: + type: number + occupancy_rate_15s: + type: number + occupancy_rate_5m: + type: number + occupancy_rate_30m: + type: number + memory: + type: number + vcpus: + type: number + memory_usage: + type: number + wm_memory_usage: + type: number + required: + - worker + - worker_instance + - ping_at + - started_at + - ip + - jobs_executed + - worker_group + - wm_version + UserWorkspaceList: + type: object + properties: + email: + type: string + workspaces: + type: array + items: + type: object + properties: + id: + type: string + name: + type: string + username: + type: string + required: + - id + - name + - username + required: + - email + - workspaces + + CreateWorkspace: + type: object + properties: + id: + type: string + name: + type: string + username: + type: string + required: + - id + - name + + Workspace: + type: object + properties: + id: + type: string + name: + type: string + owner: + type: string + domain: + type: string + required: + - id + - name + - owner + + WorkspaceInvite: + type: object + properties: + workspace_id: + type: string + email: + type: string + is_admin: + type: boolean + operator: + type: boolean + required: + - workspace_id + - email + - is_admin + - operator + + GlobalUserInfo: + type: object + properties: + email: + type: string + login_type: + type: string + enum: ["password", "github"] + super_admin: + type: boolean + verified: + type: boolean + name: + type: string + company: + type: string + username: + type: string + operator_only: + type: boolean + + required: + - email + - login_type + - super_admin + - verified + + Flow: + allOf: + - $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" + - $ref: "#/components/schemas/FlowMetadata" + + ExtraPerms: + type: object + additionalProperties: + type: boolean + + FlowMetadata: + type: object + properties: + workspace_id: + type: string + path: + type: string + edited_by: + type: string + edited_at: + type: string + format: date-time + archived: + type: boolean + extra_perms: + $ref: "#/components/schemas/ExtraPerms" + starred: + type: boolean + draft_only: + type: boolean + tag: + type: string + ws_error_handler_muted: + type: boolean + priority: + type: integer + dedicated_worker: + type: boolean + timeout: + type: number + visible_to_runner_only: + type: boolean + required: + - path + - edited_by + - edited_at + - archived + - extra_perms + + OpenFlowWPath: + allOf: + - $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" + - type: object + properties: + path: + type: string + tag: + type: string + ws_error_handler_muted: + type: boolean + priority: + type: integer + dedicated_worker: + type: boolean + timeout: + type: number + visible_to_runner_only: + type: boolean + required: + - path + + FlowPreview: + type: object + properties: + value: + $ref: "../../openflow.openapi.yaml#/components/schemas/FlowValue" + path: + type: string + args: + $ref: "#/components/schemas/ScriptArgs" + tag: + type: string + restarted_from: + $ref: "#/components/schemas/RestartedFrom" + + required: + - value + - content + - args + + RestartedFrom: + type: object + properties: + flow_job_id: + type: string + format: uuid + step_id: + type: string + branch_or_iteration_n: + type: integer + + Policy: + type: object + properties: + triggerables: + type: object + additionalProperties: + type: object + triggerables_v2: + type: object + additionalProperties: + type: object + s3_inputs: + type: array + items: + type: object + execution_mode: + type: string + enum: [viewer, publisher, anonymous] + on_behalf_of: + type: string + on_behalf_of_email: + type: string + + ListableApp: + type: object + properties: + id: + type: integer + workspace_id: + type: string + path: + type: string + summary: + type: string + version: + type: integer + extra_perms: + type: object + additionalProperties: + type: boolean + starred: + type: boolean + edited_at: + type: string + format: date-time + execution_mode: + type: string + enum: [viewer, publisher, anonymous] + required: + - id + - workspace_id + - path + - summary + - version + - extra_perms + - edited_at + - execution_mode + + ListableRawApp: + type: object + properties: + workspace_id: + type: string + path: + type: string + summary: + type: string + extra_perms: + type: object + additionalProperties: + type: boolean + starred: + type: boolean + version: + type: number + edited_at: + type: string + format: date-time + required: + - workspace_id + - path + - summary + - extra_perms + - version + - edited_at + + AppWithLastVersion: + type: object + properties: + id: + type: integer + workspace_id: + type: string + path: + type: string + summary: + type: string + versions: + type: array + items: + type: integer + created_by: + type: string + created_at: + type: string + format: date-time + value: + type: object + policy: + $ref: "#/components/schemas/Policy" + execution_mode: + type: string + enum: [viewer, publisher, anonymous] + extra_perms: + type: object + additionalProperties: + type: boolean + required: + - id + - workspace_id + - path + - summary + - versions + - created_by + - created_at + - value + - policy + - execution_mode + - extra_perms + + AppWithLastVersionWDraft: + allOf: + - $ref: "#/components/schemas/AppWithLastVersion" + - type: object + properties: + draft_only: + type: boolean + draft: {} + + AppHistory: + type: object + properties: + version: + type: integer + deployment_msg: + type: string + required: + - version + + FlowVersion: + type: object + properties: + id: + type: integer + created_at: + type: string + format: date-time + deployment_msg: + type: string + required: + - id + - created_at + + SlackToken: + type: object + properties: + access_token: + type: string + team_id: + type: string + team_name: + type: string + bot: + type: object + properties: + bot_access_token: + type: string + required: + - access_token + - team_id + - team_name + - bot + + TokenResponse: + type: object + properties: + access_token: + type: string + expires_in: + type: integer + refresh_token: + type: string + scope: + type: array + items: + type: string + + required: + - access_token + + HubScriptKind: + name: kind + schema: + type: string + enum: [script, failure, trigger, approval] + + PolarsClientKwargs: + type: object + properties: + region_name: + type: string + required: + - region_name + + LargeFileStorage: + type: object + properties: + type: + type: string + enum: + [ + "S3Storage", + "AzureBlobStorage", + "AzureWorkloadIdentity", + "S3AwsOidc", + ] + s3_resource_path: + type: string + azure_blob_resource_path: + type: string + public_resource: + type: boolean + secondary_storage: + type: object + additionalProperties: + type: object + properties: + type: + type: string + enum: + [ + "S3Storage", + "AzureBlobStorage", + "AzureWorkloadIdentity", + "S3AwsOidc", + ] + s3_resource_path: + type: string + azure_blob_resource_path: + type: string + public_resource: + type: boolean + + WindmillLargeFile: + type: object + properties: + s3: + type: string + required: + - s3 + + WindmillFileMetadata: + type: object + properties: + mime_type: + type: string + size_in_bytes: + type: integer + last_modified: + type: string + format: date-time + expires: + type: string + format: date-time + version_id: + type: string + + WindmillFilePreview: + type: object + properties: + msg: + type: string + content: + type: string + content_type: + type: string + enum: ["RawText", "Csv", "Parquet", "Unknown"] + required: + - content_type + + S3Resource: + type: object + properties: + bucket: + type: string + region: + type: string + endPoint: + type: string + useSSL: + type: boolean + accessKey: + type: string + secretKey: + type: string + pathStyle: + type: boolean + required: + - bucket + - region + - endPoint + - useSSL + - pathStyle + + WorkspaceGitSyncSettings: + type: object + properties: + include_path: + type: array + items: + type: string + include_type: + type: array + items: + type: string + enum: + - script + - flow + - app + - folder + - resource + - variable + - secret + - resourcetype + - schedule + - user + - group + repositories: + type: array + items: + $ref: "#/components/schemas/GitRepositorySettings" + + WorkspaceDeployUISettings: + type: object + properties: + include_path: + type: array + items: + type: string + include_type: + type: array + items: + type: string + enum: + - script + - flow + - app + - resource + - variable + - secret + + WorkspaceDefaultScripts: + type: object + properties: + order: + type: array + items: + type: string + hidden: + type: array + items: + type: string + default_script_content: + additionalProperties: + type: string + + GitRepositorySettings: + type: object + properties: + script_path: + type: string + git_repo_resource_path: + type: string + use_individual_branch: + type: boolean + group_by_folder: + type: boolean + exclude_types_override: + type: array + items: + type: string + enum: + - script + - flow + - app + - folder + - resource + - variable + - secret + - resourcetype + - schedule + - user + - group + required: + - script_path + - git_repo_resource_path + + UploadFilePart: + type: object + properties: + part_number: + type: integer + tag: + type: string + required: + - part_number + - tag + + MetricMetadata: + type: object + properties: + id: + type: string + name: + type: string + required: + - id + + ScalarMetric: + type: object + properties: + metric_id: + type: string + value: + type: number + required: + - id + - value + + TimeseriesMetric: + type: object + properties: + metric_id: + type: string + values: + type: array + items: + $ref: "#/components/schemas/MetricDataPoint" + required: + - id + - values + + MetricDataPoint: + type: object + properties: + timestamp: + type: string + format: date-time + value: + type: number + required: + - timestamp + - value + + RawScriptForDependencies: + type: object + properties: + raw_code: + type: string + path: + type: string + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + php, + rust, + ansible, + ] + required: + - raw_code + - path + - language + + ConcurrencyGroup: + type: object + properties: + concurrency_key: + type: string + total_running: + type: number + required: + - concurrency_key + - total_running + + ExtendedJobs: + type: object + properties: + jobs: + type: array + items: + $ref: "#/components/schemas/Job" + obscured_jobs: + type: array + items: + $ref : "#/components/schemas/ObscuredJob" + omitted_obscured_jobs: + description: "Obscured jobs omitted for security because of too specific filtering" + type: boolean + required: + - jobs + - obscured_jobs + + ExportedUser: + type: object + properties: + email: + type: string + password_hash: + type: string + super_admin: + type: boolean + verified: + type: boolean + name: + type: string + company: + type: string + first_time_user: + type: boolean + username: + type: string + required: + - email + - super_admin + - verified + - first_time_user + + GlobalSetting: + type: object + properties: + name: + type: string + value: + type: object + required: + - name + - value + + Config: + type: object + properties: + name: + type: string + config: + type: object + required: + - name + + ExportedInstanceGroup: + type: object + properties: + name: + type: string + summary: + type: string + emails: + type: array + items: + type: string + id: + type: string + scim_display_name: + type: string + external_id: + type: string + required: + - name + + JobSearchHit: + type: object + properties: + dancer: + type: string + + LogSearchHit: + type: object + properties: + dancer: + type: string + + AutoscalingEvent: + type: object + properties: + id: + type: integer + format: int64 + worker_group: + type: string + event_type: + type: string + desired_workers: + type: integer + reason: + type: string + applied_at: + type: string + format: date-time + + CriticalAlert: + type: object + properties: + id: + type: integer + description: Unique identifier for the alert + alert_type: + type: string + description: Type of alert (e.g., critical_error) + message: + type: string + description: The message content of the alert + created_at: + type: string + format: date-time + description: Time when the alert was created + acknowledged: + type: boolean + nullable: true + description: Acknowledgment status of the alert, can be true, false, or null if not set + workspace_id: + type: string + nullable: true + description: Workspace id if the alert is in the scope of a workspace diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index e9addb0522bbc..7ed3fe6ce2057 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -7749,6 +7749,23 @@ paths: schema: type: string + /w/{workspace}/database_triggers/get/{path}: + get: + summary: get database trigger + operationId: getDatabaseTrigger + tags: + - database_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: get database trigger + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseTrigger" + /w/{workspace}/database_triggers/list: get: summary: list database triggers @@ -8698,6 +8715,7 @@ paths: http_trigger, websocket_trigger, database_trigger + database_trigger ] responses: "200": @@ -11911,16 +11929,36 @@ components: - is_flow - args - HttpTrigger: + TriggerExtraProperty: type: object properties: - path: + email: type: string - edited_by: + extra_perms: + type: object + additionalProperties: + type: boolean + workspace_id: type: string + edited_by: + type: string edited_at: + type: string + format: date-time + required: + - email + - extra_perms + - workspace_id + - edited_by + - edited_at + + HttpTrigger: + allOf: + - $ref: '#/components/schemas/TriggerExtraProperty' + type: object + properties: + path: type: string - format: date-time script_path: type: string route_path: @@ -11938,14 +11976,6 @@ components: - s3 is_flow: type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - email: - type: string - workspace_id: - type: string http_method: type: string enum: @@ -12082,29 +12112,18 @@ components: type: number WebsocketTrigger: + allOf: + - $ref: '#/components/schemas/TriggerExtraProperty' type: object properties: path: type: string - edited_by: - type: string - edited_at: - type: string - format: date-time script_path: type: string url: type: string is_flow: type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - email: - type: string - workspace_id: - type: string server_id: type: string last_server_ping: @@ -12266,12 +12285,38 @@ components: DatabaseToTrack: type: object properties: - username: string - password: string - host: string - port: number + username: + type: string + password: + type: string + host: + type: string + port: + type: number + required: + - username + - host + - port + + DatabaseToTrackWithoutPass: + allOf: + $ref: "#/components/schemas/DatabaseToTrack" + type: object + properties: + username: + type: string + host: + type: string + port: + type: number + required: + - username + - host + - port DatabaseTrigger: + allOf: + - $ref: '#/components/schemas/TriggerExtraProperty' type: object properties: path: @@ -12283,16 +12328,17 @@ components: enabled: type: boolean database: - $ref: "#/components/schemas/DatabaseToTrack" - required: - - username - - host - - port + $ref: "#/components/schemas/DatabaseToTrackWithoutPass" + required: + - username + - host + - port table_to_track: type: array items: $ref: "#/components/schemas/TableToTrack" - + error: + type: string required: - path - script_path diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs index 9b22b18a0b916..ad0bb60ae93f4 100644 --- a/backend/windmill-api/src/database_triggers.rs +++ b/backend/windmill-api/src/database_triggers.rs @@ -5,11 +5,18 @@ use axum::{ }; use http::StatusCode; use serde::{Deserialize, Serialize}; -use windmill_common::{db::UserDB, error, utils::StripPath, worker::CLOUD_HOSTED}; +use sqlx::FromRow; +use windmill_audit::{audit_ee::audit_log, ActionKind}; +use windmill_common::{ + db::UserDB, + error::{self, JsonResult}, + utils::{not_found_if_none, StripPath}, + worker::CLOUD_HOSTED, +}; -use crate::db::ApiAuthed; +use crate::db::{ApiAuthed, DB}; -#[derive(Serialize, Deserialize)] +#[derive(FromRow, Serialize, Deserialize)] struct Database { username: String, password: Option, @@ -23,16 +30,29 @@ impl Database { } } -#[derive(Serialize, Deserialize)] +#[derive(FromRow, Serialize, Deserialize)] struct TableTrack { table_name: String, columns_name: Option>, + columns_name: Option>, } #[derive(Deserialize)] -struct EditDatabaseTrigger {} +struct EditDatabaseTrigger { + path: Option, + script_path: Option, + is_flow: Option, + enabled: Option, + database: Option, + table_to_track: Option>, +} #[derive(Deserialize, Serialize)] +struct NewDatabaseTrigger { + path: String, + script_path: String, + is_flow: bool, + enabled: bool, struct NewDatabaseTrigger { path: String, script_path: String, @@ -42,7 +62,7 @@ struct NewDatabaseTrigger { table_to_track: Option>, } -#[derive(Deserialize, Serialize)] +#[derive(FromRow, Deserialize, Serialize)] struct DatabaseTrigger { database: Database, table_to_track: Option>, @@ -62,6 +82,7 @@ async fn create_database_trigger( Extension(user_db): Extension, Path(w_id): Path, Json(new_database_trigger): Json, + Json(new_database_trigger): Json, ) -> error::Result<(StatusCode, String)> { let NewDatabaseTrigger { database, table_to_track, path, script_path, enabled, is_flow } = new_database_trigger; @@ -72,6 +93,15 @@ async fn create_database_trigger( } let mut tx = user_db.begin(&authed).await?; + let NewDatabaseTrigger { database, table_to_track, path, script_path, enabled, is_flow } = + new_database_trigger; + if *CLOUD_HOSTED { + return Err(error::Error::BadRequest( + "Database triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), + )); + } + let mut tx = user_db.begin(&authed).await?; + Ok((StatusCode::OK, "OK".to_string())) } @@ -89,17 +119,62 @@ async fn get_database_trigger( Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> error::JsonResult { - todo!() - todo!() + let mut tx = user_db.begin(&authed).await?; + let path = path.to_path(); + let trigger = sqlx::query_as::<_, DatabaseTrigger>( + r#"SELECT * + FROM database_trigger + WHERE workspace_id = $1 AND path = $2"#, + ) + .bind(w_id) + .bind(path) + .fetch_optional(&mut *tx) + .await?; + tx.commit().await?; + + let trigger = not_found_if_none(trigger, "Trigger", path)?; + + Ok(Json(trigger)) } async fn update_database_trigger( authed: ApiAuthed, Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, - Json(ct): Json, + Json(database_trigger): Json, ) -> error::Result { - Ok(String::new()) + let workspace_path = path.to_path(); + let EditDatabaseTrigger { script_path, path, is_flow, enabled, database, table_to_track } = + database_trigger; + let mut tx = user_db.begin(&authed).await?; + + /*sqlx::query!( + "UPDATE database_trigger SET script_path = $1, path = $2, is_flow = $3, edited_by = $4, email = $5, edited_at = now(), error = NULL + WHERE workspace_id = $6 AND path = $7", + script_path, + path, + is_flow, + &authed.username, + &authed.email, + w_id, + workspace_path, + ) + .execute(&mut *tx).await?;*/ + + audit_log( + &mut *tx, + &authed, + "database_triggers.update", + ActionKind::Create, + &w_id, + Some(workspace_path), + None, + ) + .await?; + + tx.commit().await?; + + Ok(workspace_path.to_string()) } async fn delete_database_trigger( @@ -107,7 +182,50 @@ async fn delete_database_trigger( Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> error::Result { - Ok(format!("Databse trigger deleted")) + let path = path.to_path(); + let mut tx = user_db.begin(&authed).await?; + sqlx::query!( + "DELETE FROM database_trigger WHERE workspace_id = $1 AND path = $2", + w_id, + path, + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "database_triggers.delete", + ActionKind::Delete, + &w_id, + Some(path), + None, + ) + .await?; + + tx.commit().await?; + + Ok(format!("Database trigger {path} deleted")) +} + +async fn exists_database_trigger( + Extension(db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult { + let path = path.to_path(); + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM database_trigger WHERE path = $1 AND workspace_id = $2)", + path, + w_id, + ) + .fetch_one(&db) + .await? + .unwrap_or(false); + Ok(Json(exists)) +} + +async fn set_enabled() -> error::Result { + Ok(format!("Ok")) } pub fn workspaced_service() -> Router { @@ -117,4 +235,6 @@ pub fn workspaced_service() -> Router { .route("/get/*path", get(get_database_trigger)) .route("/update/*path", post(update_database_trigger)) .route("/delete/*path", delete(delete_database_trigger)) + .route("/exists/*path", get(exists_database_trigger)) + .route("/setenabled/*path", post(set_enabled)) } diff --git a/frontend/openapi-ts-error-1732447138336.log b/frontend/openapi-ts-error-1732447138336.log new file mode 100644 index 0000000000000..9e7156b4c2df0 --- /dev/null +++ b/frontend/openapi-ts-error-1732447138336.log @@ -0,0 +1,12 @@ +Token "DatabaseToTrackWithoutPass" does not exist. +JSONParserError: Token "DatabaseToTrackWithoutPass" does not exist. + at Pointer.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:103:23) + at $Ref.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28) + at $Refs._resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:158:21) + at inventory$Ref (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:116:27) + at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:91:21) + at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) + at inventory$Ref (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:153:9) + at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:91:21) + at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) + at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) \ No newline at end of file diff --git a/frontend/src/lib/components/Path.svelte b/frontend/src/lib/components/Path.svelte index 2d112387af4c7..0c22398499a49 100644 --- a/frontend/src/lib/components/Path.svelte +++ b/frontend/src/lib/components/Path.svelte @@ -14,7 +14,10 @@ ScriptService, HttpTriggerService, VariableService, - WebsocketTriggerService + WebsocketTriggerService, + + DatabaseTriggerService + } from '$lib/gen' import { superadmin, userStore, workspaceStore } from '$lib/stores' import { createEventDispatcher, getContext } from 'svelte' @@ -37,6 +40,7 @@ | 'raw_app' | 'http_trigger' | 'websocket_trigger' + | 'database_trigger' let meta: Meta | undefined = undefined export let fullNamePlaceholder: string | undefined = undefined export let namePlaceholder = '' @@ -225,6 +229,11 @@ workspace: $workspaceStore!, path: path }) + } else if (kind == 'database_trigger') { + return await DatabaseTriggerService.existsDatabaseTrigger({ + workspace: $workspaceStore!, + path: path + }) } else { return false } diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index 600cd4c1803ca..7f22fc6be515a 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -1,30 +1,17 @@ @@ -261,13 +147,13 @@ checked={enabled} options={{ right: 'enable', left: 'disable' }} on:change={async (e) => { - await WebsocketTriggerService.setWebsocketTriggerEnabled({ + await DatabaseTriggerService.setDatabaseTriggerEnabled({ path: initialPath, workspace: $workspaceStore ?? '', requestBody: { enabled: e.detail } }) sendUserToast( - `${e.detail ? 'enabled' : 'disabled'} websocket trigger ${initialPath}` + `${e.detail ? 'enabled' : 'disabled'} database trigger ${initialPath}` ) }} /> @@ -275,11 +161,7 @@ {/if} -
- {/each} - -
- -
-
- - -
-

- Filters will limit the execution of the trigger to only messages that match all - criteria.
- The JSON filter checks if the value at the key is equal or a superset of the filter value. -

-
- {#each filters as v, i} -
-
-
- -
- - - -
- -
- {/each} - -
- -
-
-
{/if} diff --git a/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte index 432e206b54e61..762b530768a01 100644 --- a/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/database_triggers/+page.svelte @@ -1,5 +1,5 @@ @@ -172,6 +171,13 @@ {#if drawerLoading} {:else} + + {#if edit} + Changes can take up to 30 seconds to take effect. + {:else} + New database triggers can take up to 30 seconds to start listening. + {/if} +
- +
+

+ Choose what kind of database transaction you want to track +

+ + + + + +
+
+

+ Write which table from the current selected database resource must be tracked +

+
{/if} From 3cdf0a2cc034cdb6fdb6539c4fa3fd721f8e59c5 Mon Sep 17 00:00:00 2001 From: dieriba Date: Wed, 27 Nov 2024 17:32:26 +0100 Subject: [PATCH 11/50] feat: :construction: database_triggers --- .../20241123152203_database_triggers.up.sql | 2 +- backend/windmill-api/src/database_triggers.rs | 101 +++++++++++++++--- backend/windmill-api/src/triggers.rs | 12 +++ .../DatabaseTriggerEditorInner.svelte | 14 ++- .../WebsocketTriggerEditorInner.svelte | 10 +- 5 files changed, 113 insertions(+), 26 deletions(-) diff --git a/backend/migrations/20241123152203_database_triggers.up.sql b/backend/migrations/20241123152203_database_triggers.up.sql index 42d0c6522673f..d4026394391ff 100644 --- a/backend/migrations/20241123152203_database_triggers.up.sql +++ b/backend/migrations/20241123152203_database_triggers.up.sql @@ -8,7 +8,7 @@ CREATE TABLE database_trigger( email VARCHAR(255) NOT NULL, edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), extra_perms JSONB NOT NULL DEFAULT '{}', - database JSONB NOT NULL, + database VARCHAR(255) NOT NULL, table_to_track JSONB[], error TEXT NULL, server_id VARCHAR(50) NULL, diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs index b7010a120a7f2..b3aefc1e6869d 100644 --- a/backend/windmill-api/src/database_triggers.rs +++ b/backend/windmill-api/src/database_triggers.rs @@ -4,15 +4,17 @@ use axum::{ Extension, Json, Router, }; use http::StatusCode; +use itertools::Itertools; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; +use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::FromRow; use windmill_audit::{audit_ee::audit_log, ActionKind}; use windmill_common::{ db::UserDB, error::{self, JsonResult}, - utils::{not_found_if_none, StripPath}, + utils::{not_found_if_none, paginate, Pagination, StripPath}, worker::CLOUD_HOSTED, INSTANCE_NAME, }; @@ -48,11 +50,10 @@ struct TableToTrack { #[derive(Deserialize)] struct EditDatabaseTrigger { - path: Option, - script_path: Option, - is_flow: Option, - enabled: Option, - database: Option, + path: String, + script_path: String, + is_flow: bool, + database: String, table_to_track: Option>, } @@ -63,7 +64,7 @@ struct NewDatabaseTrigger { script_path: String, is_flow: bool, enabled: bool, - database: Database, + database: String, table_to_track: Option>, } @@ -112,9 +113,41 @@ async fn create_database_trigger( "Database triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), )); } + let table_to_track = table_to_track.map(|table_to_track| { + table_to_track + .into_iter() + .map(sqlx::types::Json) + .collect_vec() + }); + let mut tx = user_db.begin(&authed).await?; + sqlx::query_as::<_, DatabaseTrigger>("INSERT INTO database_trigger (workspace_id, path, script_path, is_flow, email, enabled, database, table_to_track, edited_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now()) RETURNING *") + .bind(&w_id) + .bind(&path) + .bind(script_path) + .bind(is_flow) + .bind(&authed.email) + .bind(enabled) + .bind(database) + .bind(table_to_track) + .bind(&authed.username) + .fetch_one(&mut *tx) + .await?; - Ok((StatusCode::OK, "OK".to_string())) + audit_log( + &mut *tx, + &authed, + "database_triggers.create", + ActionKind::Create, + &w_id, + Some(path.as_str()), + None, + ) + .await?; + + tx.commit().await?; + + Ok((StatusCode::CREATED, path.to_string())) } async fn list_database_triggers( @@ -123,7 +156,33 @@ async fn list_database_triggers( Path(w_id): Path, Query(lst): Query, ) -> error::JsonResult> { - Ok(Json(Vec::new())) + let mut tx = user_db.begin(&authed).await?; + let (per_page, offset) = paginate(Pagination { per_page: lst.per_page, page: lst.page }); + let mut sqlb = SqlBuilder::select_from("database_trigger") + .field("*") + .order_by("edited_at", true) + .and_where("workspace_id = ?".bind(&w_id)) + .offset(offset) + .limit(per_page) + .clone(); + if let Some(path) = lst.path { + sqlb.and_where_eq("script_path", "?".bind(&path)); + } + if let Some(is_flow) = lst.is_flow { + sqlb.and_where_eq("is_flow", "?".bind(&is_flow)); + } + if let Some(path_start) = &lst.path_start { + sqlb.and_where_like_left("path", path_start); + } + let sql = sqlb + .sql() + .map_err(|e| error::Error::InternalErr(e.to_string()))?; + let rows = sqlx::query_as::<_, DatabaseTrigger>(&sql) + .fetch_all(&mut *tx) + .await?; + tx.commit().await?; + + Ok(Json(rows)) } async fn get_database_trigger( @@ -156,22 +215,31 @@ async fn update_database_trigger( Json(database_trigger): Json, ) -> error::Result { let workspace_path = path.to_path(); - let EditDatabaseTrigger { script_path, path, is_flow, enabled, database, table_to_track } = + let EditDatabaseTrigger { script_path, path, is_flow, database, table_to_track } = database_trigger; let mut tx = user_db.begin(&authed).await?; - /*sqlx::query!( - "UPDATE database_trigger SET script_path = $1, path = $2, is_flow = $3, edited_by = $4, email = $5, edited_at = now(), error = NULL - WHERE workspace_id = $6 AND path = $7", + let table_to_track = table_to_track.map(|table_to_track| { + table_to_track + .into_iter() + .map(sqlx::types::Json) + .collect_vec() + }); + + sqlx::query!( + "UPDATE database_trigger SET script_path = $1, path = $2, is_flow = $3, edited_by = $4, email = $5, database = $6, table_to_track = $7, edited_at = now(), error = NULL + WHERE workspace_id = $8 AND path = $9", script_path, path, is_flow, &authed.username, &authed.email, + database, + table_to_track, w_id, workspace_path, ) - .execute(&mut *tx).await?;*/ + .execute(&mut *tx).await?; audit_log( &mut *tx, @@ -179,7 +247,7 @@ async fn update_database_trigger( "database_triggers.update", ActionKind::Create, &w_id, - Some(workspace_path), + Some(&path), None, ) .await?; @@ -356,7 +424,7 @@ pub async fn start_database( db: DB, rsmq: Option, mut killpill_rx: tokio::sync::broadcast::Receiver<()>, -) -> () { +) { tokio::spawn(async move { listen_to_unlistened_database_events(&db, &rsmq, &killpill_rx).await; loop { @@ -379,7 +447,6 @@ pub async fn can_be_listened( ) -> JsonResult { let mut tx = user_db.begin(&authed).await?; - Ok(Json(true)) } diff --git a/backend/windmill-api/src/triggers.rs b/backend/windmill-api/src/triggers.rs index 337a20364b598..f8261313d0ec3 100644 --- a/backend/windmill-api/src/triggers.rs +++ b/backend/windmill-api/src/triggers.rs @@ -18,6 +18,7 @@ pub struct TriggersCount { webhook_count: i64, email_count: i64, websocket_count: i64, + database_count: i64 } pub(crate) async fn get_triggers_count_internal( db: &DB, @@ -64,6 +65,16 @@ pub(crate) async fn get_triggers_count_internal( .await? .unwrap_or(0); + let database_count = sqlx::query_scalar!( + "SELECT COUNT(*) FROM database_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3", + path, + is_flow, + w_id + ) + .fetch_one(db) + .await? + .unwrap_or(0); + let webhook_count = (if is_flow { sqlx::query_scalar!( "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]", @@ -105,6 +116,7 @@ pub(crate) async fn get_triggers_count_internal( webhook_count, email_count, websocket_count, + database_count })) } diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index 0f94496988c79..535503a332b36 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -16,6 +16,7 @@ import ResourcePicker from '$lib/components/ResourcePicker.svelte' import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' + import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte' let drawer: Drawer let is_flow: boolean = false @@ -127,6 +128,7 @@ } let selected: 'insert' | 'updated' | 'delete' = 'insert' + let tables: string[] = [] @@ -230,10 +232,16 @@

- Write which table from the current selected database resource must be tracked + Tables will limit the execution of the trigger to only the given tables.
+ If no tables table are choosen, this will be triggered for all tables.

+
{/if} diff --git a/frontend/src/lib/components/triggers/websocket_triggers/WebsocketTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/websocket_triggers/WebsocketTriggerEditorInner.svelte index 600cd4c1803ca..2c94f94d009f5 100644 --- a/frontend/src/lib/components/triggers/websocket_triggers/WebsocketTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/websocket_triggers/WebsocketTriggerEditorInner.svelte @@ -428,7 +428,7 @@
From fabe469fcf102c7678a5510474ea676fcc67c48a Mon Sep 17 00:00:00 2001 From: dieriba Date: Sat, 30 Nov 2024 01:50:25 +0100 Subject: [PATCH 12/50] feat: :construction: update openapi yaml, prettied websocker trigger --- .../20241123152203_database_triggers.down.sql | 3 +- .../20241123152203_database_triggers.up.sql | 2 + backend/openapi-deref.yaml | 0 backend/openapi.yaml | 13333 ---------------- backend/windmill-api/openapi.yaml | 17 +- backend/windmill-api/src/database_triggers.rs | 60 +- .../DatabaseTriggerEditorInner.svelte | 54 +- .../WebsocketTriggerEditorInner.svelte | 10 +- 8 files changed, 103 insertions(+), 13376 deletions(-) delete mode 100644 backend/openapi-deref.yaml delete mode 100644 backend/openapi.yaml diff --git a/backend/migrations/20241123152203_database_triggers.down.sql b/backend/migrations/20241123152203_database_triggers.down.sql index 2900ae5dcccb1..7c18c0075bdb7 100644 --- a/backend/migrations/20241123152203_database_triggers.down.sql +++ b/backend/migrations/20241123152203_database_triggers.down.sql @@ -1,2 +1,3 @@ -- Add down migration script here -DROP TABLE IF EXISTS database_trigger; \ No newline at end of file +DROP TABLE IF EXISTS database_trigger; +DROP TYPE IF EXISTS transaction; \ No newline at end of file diff --git a/backend/migrations/20241123152203_database_triggers.up.sql b/backend/migrations/20241123152203_database_triggers.up.sql index d4026394391ff..99622e4214076 100644 --- a/backend/migrations/20241123152203_database_triggers.up.sql +++ b/backend/migrations/20241123152203_database_triggers.up.sql @@ -1,4 +1,5 @@ -- Add up migration script here +CREATE TYPE transaction AS ENUM ('Insert', 'Update', 'Delete'); CREATE TABLE database_trigger( path VARCHAR(255) NOT NULL, script_path VARCHAR(255) NOT NULL, @@ -9,6 +10,7 @@ CREATE TABLE database_trigger( edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), extra_perms JSONB NOT NULL DEFAULT '{}', database VARCHAR(255) NOT NULL, + transaction_type transaction NOT NULL, table_to_track JSONB[], error TEXT NULL, server_id VARCHAR(50) NULL, diff --git a/backend/openapi-deref.yaml b/backend/openapi-deref.yaml deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/backend/openapi.yaml b/backend/openapi.yaml deleted file mode 100644 index 91c19fc75f237..0000000000000 --- a/backend/openapi.yaml +++ /dev/null @@ -1,13333 +0,0 @@ -openapi: "3.0.3" - -info: - version: 1.425.1 - title: Windmill API - - contact: - name: Windmill Team - email: contact@windmill.dev - url: https://windmill.dev - - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0.html - - x-logo: - url: https://windmill.dev/img/windmill.svg -externalDocs: - description: documentation portal - url: https://windmill.dev - -servers: - - url: /api - -security: - - bearerAuth: [] - - cookieAuth: [] - -paths: - /version: - get: - summary: get backend version - operationId: backendVersion - tags: - - settings - responses: - "200": - description: git version of backend - content: - text/plain: - schema: - type: string - - /uptodate: - get: - summary: is backend up to date - operationId: backendUptodate - tags: - - settings - responses: - "200": - description: is backend up to date - content: - text/plain: - schema: - type: string - - /ee_license: - get: - summary: get license id - operationId: getLicenseId - tags: - - settings - responses: - "200": - description: get license id (empty if not ee) - content: - text/plain: - schema: - type: string - - /openapi.yaml: - get: - summary: get openapi yaml spec - operationId: getOpenApiYaml - tags: - - settings - responses: - "200": - description: openapi yaml file content - content: - text/plain: - schema: - type: string - - /w/{workspace}/audit/get/{id}: - get: - summary: get audit log (requires admin privilege) - operationId: getAuditLog - tags: - - audit - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/PathId" - responses: - "200": - description: an audit log - content: - application/json: - schema: - $ref: "#/components/schemas/AuditLog" - - /w/{workspace}/audit/list: - get: - summary: list audit logs (requires admin privilege) - operationId: listAuditLogs - tags: - - audit - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - $ref: "#/components/parameters/Before" - - $ref: "#/components/parameters/After" - - $ref: "#/components/parameters/Username" - - $ref: "#/components/parameters/Operation" - - name: operations - in: query - description: comma separated list of exact operations to include - schema: - type: string - - name: exclude_operations - in: query - description: comma separated list of operations to exclude - schema: - type: string - - $ref: "#/components/parameters/ResourceName" - - $ref: "#/components/parameters/ActionKind" - - responses: - "200": - description: a list of audit logs - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/AuditLog" - - /auth/login: - post: - security: [] - summary: login with password - operationId: login - tags: - - user - requestBody: - description: credentials - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/Login" - - responses: - "200": - description: > - Successfully authenticated. The session ID is returned in a cookie - named `token` and as plaintext response. Preferred method of - authorization is through the bearer token. The cookie is only for - browser convenience. - - headers: - Set-Cookie: - schema: - type: string - example: token=abcde12345; Path=/; HttpOnly - content: - text/plain: - schema: - type: string - - /auth/logout: - post: - security: [] - summary: logout - operationId: logout - tags: - - user - - responses: - "200": - description: clear cookies and clear token (if applicable) - headers: - Set-Cookie: - schema: - type: string - content: - text/plain: - schema: - type: string - - /w/{workspace}/users/get/{username}: - get: - summary: get user (require admin privilege) - operationId: getUser - tags: - - user - - admin - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: username - in: path - required: true - schema: - type: string - responses: - "200": - description: user created - content: - application/json: - schema: - $ref: "#/components/schemas/User" - - /w/{workspace}/users/update/{username}: - post: - summary: update user (require admin privilege) - operationId: updateUser - tags: - - user - - admin - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: username - in: path - required: true - schema: - type: string - requestBody: - description: new user - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditWorkspaceUser" - responses: - "200": - description: edited user - content: - text/plain: - schema: - type: string - - /w/{workspace}/users/is_owner/{path}: - get: - summary: is owner of path - operationId: isOwnerOfPath - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: is owner - content: - application/json: - schema: - type: boolean - - /users/setpassword: - post: - summary: set password - operationId: setPassword - tags: - - user - requestBody: - description: set password - required: true - content: - application/json: - schema: - type: object - properties: - password: - type: string - required: - - password - responses: - "200": - description: password set - content: - text/plain: - schema: - type: string - - /users/set_password_of/{user}: - post: - summary: set password for a specific user (require super admin) - operationId: setPasswordForUser - tags: - - user - parameters: - - name: user - in: path - required: true - schema: - type: string - requestBody: - description: set password - required: true - content: - application/json: - schema: - type: object - properties: - password: - type: string - required: - - password - responses: - "200": - description: password set - content: - text/plain: - schema: - type: string - - /users/set_login_type/{user}: - post: - summary: set login type for a specific user (require super admin) - operationId: setLoginTypeForUser - tags: - - user - parameters: - - name: user - in: path - required: true - schema: - type: string - requestBody: - description: set login type - required: true - content: - application/json: - schema: - type: object - properties: - login_type: - type: string - required: - - login_type - responses: - "200": - description: login type set - content: - text/plain: - schema: - type: string - - - /users/create: - post: - summary: create user - operationId: createUserGlobally - tags: - - user - requestBody: - description: user info - required: true - content: - application/json: - schema: - type: object - properties: - email: - type: string - password: - type: string - super_admin: - type: boolean - name: - type: string - company: - type: string - required: - - email - - password - - super_admin - responses: - "201": - description: user created - content: - text/plain: - schema: - type: string - - /users/update/{email}: - post: - summary: global update user (require super admin) - operationId: globalUserUpdate - tags: - - user - parameters: - - name: email - in: path - required: true - schema: - type: string - requestBody: - description: new user info - required: true - content: - application/json: - schema: - type: object - properties: - is_super_admin: - type: boolean - name: - type: string - responses: - "200": - description: user updated - content: - text/plain: - schema: - type: string - - /users/username_info/{email}: - get: - summary: global username info (require super admin) - operationId: globalUsernameInfo - tags: - - user - parameters: - - name: email - in: path - required: true - schema: - type: string - responses: - "200": - description: user renamed - content: - application/json: - schema: - type: object - properties: - username: - type: string - workspace_usernames: - type: array - items: - type: object - properties: - workspace_id: - type: string - username: - type: string - required: - - workspace_id - - username - required: - - username - - workspace_usernames - - /users/rename/{email}: - post: - summary: global rename user (require super admin) - operationId: globalUserRename - tags: - - user - parameters: - - name: email - in: path - required: true - schema: - type: string - requestBody: - description: new username - required: true - content: - application/json: - schema: - type: object - properties: - new_username: - type: string - required: - - new_username - responses: - "200": - description: user renamed - content: - text/plain: - schema: - type: string - - /users/delete/{email}: - delete: - summary: global delete user (require super admin) - operationId: globalUserDelete - tags: - - user - parameters: - - name: email - in: path - required: true - schema: - type: string - responses: - "200": - description: user deleted - content: - text/plain: - schema: - type: string - /users/overwrite: - post: - summary: global overwrite users (require super admin and EE) - operationId: globalUsersOverwrite - tags: - - user - requestBody: - description: List of users - required: true - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ExportedUser" - responses: - "200": - description: Success message - content: - text/plain: - schema: - type: string - - /users/export: - get: - summary: global export users (require super admin and EE) - operationId: globalUsersExport - tags: - - user - responses: - "200": - description: exported users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ExportedUser" - - /w/{workspace}/users/delete/{username}: - delete: - summary: delete user (require admin privilege) - operationId: deleteUser - tags: - - user - - admin - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: username - in: path - required: true - schema: - type: string - responses: - "200": - description: delete user - content: - text/plain: - schema: - type: string - - /workspaces/list: - get: - summary: list all workspaces visible to me - operationId: listWorkspaces - tags: - - workspace - responses: - "200": - description: all workspaces - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Workspace" - - /workspaces/allowed_domain_auto_invite: - get: - summary: is domain allowed for auto invi - operationId: isDomainAllowed - tags: - - workspace - responses: - "200": - description: domain allowed or not - content: - application/json: - schema: - type: boolean - - /workspaces/users: - get: - summary: list all workspaces visible to me with user info - operationId: listUserWorkspaces - tags: - - workspace - responses: - "200": - description: workspace with associated username - content: - application/json: - schema: - $ref: "#/components/schemas/UserWorkspaceList" - - /workspaces/list_as_superadmin: - get: - summary: list all workspaces as super admin (require to be super admin) - operationId: listWorkspacesAsSuperAdmin - tags: - - workspace - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: workspaces - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Workspace" - - /workspaces/create: - post: - summary: create workspace - operationId: createWorkspace - tags: - - workspace - requestBody: - description: new token - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateWorkspace" - responses: - "201": - description: token created - content: - text/plain: - schema: - type: string - - /workspaces/exists: - post: - summary: exists workspace - operationId: existsWorkspace - tags: - - workspace - requestBody: - description: id of workspace - required: true - content: - application/json: - schema: - type: object - properties: - id: - type: string - required: - - id - responses: - "200": - description: status - content: - text/plain: - schema: - type: boolean - - /workspaces/exists_username: - post: - summary: exists username - operationId: existsUsername - tags: - - workspace - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - id: - type: string - username: - type: string - required: - - id - - username - responses: - "200": - description: status - content: - text/plain: - schema: - type: boolean - - /settings/global/{key}: - get: - summary: get global settings - operationId: getGlobal - tags: - - setting - parameters: - - $ref: "#/components/parameters/Key" - responses: - "200": - description: status - content: - application/json: - schema: {} - - post: - summary: post global settings - operationId: setGlobal - tags: - - setting - parameters: - - $ref: "#/components/parameters/Key" - requestBody: - description: value set - required: true - content: - application/json: - schema: - type: object - properties: - value: {} - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /settings/local: - get: - summary: get local settings - operationId: getLocal - tags: - - setting - responses: - "200": - description: status - content: - application/json: - schema: {} - - /settings/test_smtp: - post: - summary: test smtp - operationId: testSmtp - tags: - - setting - requestBody: - description: test smtp payload - required: true - content: - application/json: - schema: - type: object - properties: - to: - type: string - smtp: - type: object - properties: - host: - type: string - username: - type: string - password: - type: string - port: - type: integer - from: - type: string - tls_implicit: - type: boolean - disable_tls: - type: boolean - required: - - host - - username - - password - - port - - from - - tls_implicit - - disable_tls - required: - - to - - smtp - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /settings/test_critical_channels: - post: - summary: test critical channels - operationId: testCriticalChannels - tags: - - setting - requestBody: - description: test critical channel payload - required: true - content: - application/json: - schema: - type: array - items: - type: object - properties: - email: - type: string - slack_channel: - type: string - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /settings/critical_alerts: - get: - summary: Get all critical alerts - operationId: getCriticalAlerts - tags: - - setting - parameters: - - in: query - name: page - schema: - type: integer - default: 1 - description: The page number to retrieve (minimum value is 1) - - in: query - name: page_size - schema: - type: integer - default: 10 - maximum: 100 - description: Number of alerts per page (maximum is 100) - - in: query - name: acknowledged - schema: - type: boolean - nullable: true - description: Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts - responses: - "200": - description: Successfully retrieved all critical alerts - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/CriticalAlert' - - /settings/critical_alerts/{id}/acknowledge: - post: - summary: Acknowledge a critical alert - operationId: acknowledgeCriticalAlert - tags: - - setting - parameters: - - in: path - name: id - required: true - schema: - type: integer - description: The ID of the critical alert to acknowledge - responses: - "200": - description: Successfully acknowledged the critical alert - content: - application/json: - schema: - type: string - example: "Critical alert acknowledged" - - /settings/critical_alerts/acknowledge_all: - post: - summary: Acknowledge all unacknowledged critical alerts - operationId: acknowledgeAllCriticalAlerts - tags: - - setting - responses: - "200": - description: Successfully acknowledged all unacknowledged critical alerts. - content: - application/json: - schema: - type: string - example: "All unacknowledged critical alerts acknowledged" - - /settings/test_license_key: - post: - summary: test license key - operationId: testLicenseKey - tags: - - setting - requestBody: - description: test license key - required: true - content: - application/json: - schema: - type: object - properties: - license_key: - type: string - required: - - license_key - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - # pub use_ssl: Option, - # #[serde(rename = "accountName")] - # pub account_name: String, - # #[serde(rename = "tenantId")] - # pub tenant_id: Option, - # #[serde(rename = "clientId")] - # pub client_id: Option, - # #[serde(rename = "containerName")] - # pub container_name: String, - # #[serde(rename = "accessKey")] - # pub access_key: Option, - - /settings/test_object_storage_config: - post: - summary: test object storage config - operationId: testObjectStorageConfig - tags: - - setting - requestBody: - description: test object storage config - required: true - content: - application/json: - schema: - type: object - additionalProperties: true - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /settings/send_stats: - post: - summary: send stats - operationId: sendStats - tags: - - setting - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - - /settings/latest_key_renewal_attempt: - get: - summary: get latest key renewal attempt - operationId: getLatestKeyRenewalAttempt - tags: - - setting - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - result: - type: string - attempted_at: - type: string - format: date-time - required: - - result - - attempted_at - nullable: true - - /settings/renew_license_key: - post: - summary: renew license key - operationId: renewLicenseKey - tags: - - setting - parameters: - - name: license_key - in: query - required: false - schema: - type: string - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /settings/customer_portal: - post: - summary: create customer portal session - operationId: createCustomerPortalSession - tags: - - setting - parameters: - - name: license_key - in: query - required: false - schema: - type: string - responses: - "200": - description: url to portal - content: - text/plain: - schema: - type: string - - /saml/test_metadata: - post: - summary: test metadata - operationId: testMetadata - tags: - - setting - requestBody: - description: test metadata - required: true - content: - application/json: - schema: - type: string - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - - /settings/list_global: - get: - summary: list global settings - operationId: listGlobalSettings - tags: - - setting - responses: - "200": - description: list of settings - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/GlobalSetting" - - /users/email: - get: - summary: get current user email (if logged in) - operationId: getCurrentEmail - tags: - - user - responses: - "200": - description: user email - content: - text/plain: - schema: - type: string - - /users/refresh_token: - get: - summary: refresh the current token - operationId: refreshUserToken - tags: - - user - responses: - "200": - description: free usage - content: - text/plain: - schema: - type: string - - /users/tutorial_progress: - get: - summary: get tutorial progress - operationId: getTutorialProgress - tags: - - user - responses: - "200": - description: tutorial progress - content: - application/json: - schema: - type: object - properties: - progress: - type: integer - post: - summary: update tutorial progress - operationId: updateTutorialProgress - tags: - - user - requestBody: - description: progress update - required: true - content: - application/json: - schema: - type: object - properties: - progress: - type: integer - responses: - "200": - description: tutorial progress - content: - text/plain: - schema: - type: string - - /users/leave_instance: - post: - summary: leave instance - operationId: leaveInstance - tags: - - user - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /users/usage: - get: - summary: get current usage outside of premium workspaces - operationId: getUsage - tags: - - user - responses: - "200": - description: free usage - content: - text/plain: - schema: - type: number - - /users/all_runnables: - get: - summary: get all runnables in every workspace - operationId: getRunnable - tags: - - user - responses: - "200": - description: free all runnables - content: - application/json: - schema: - type: object - properties: - workspace: - type: string - endpoint_async: - type: string - endpoint_sync: - type: string - endpoint_openai_sync: - type: string - summary: - type: string - description: - type: string - kind: - type: string - required: - - workspace - - endpoint_async - - endpoint_sync - - endpoint_openai_sync - - summary - - kind - - /users/whoami: - get: - summary: get current global whoami (if logged in) - operationId: globalWhoami - tags: - - user - responses: - "200": - description: user email - content: - application/json: - schema: - $ref: "#/components/schemas/GlobalUserInfo" - - /users/list_invites: - get: - summary: list all workspace invites - operationId: listWorkspaceInvites - tags: - - user - responses: - "200": - description: list all workspace invites - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkspaceInvite" - - /w/{workspace}/users/whoami: - get: - summary: whoami - operationId: whoami - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: user - content: - application/json: - schema: - $ref: "#/components/schemas/User" - - /users/accept_invite: - post: - summary: accept invite to workspace - operationId: acceptInvite - tags: - - user - requestBody: - description: accept invite - required: true - content: - application/json: - schema: - type: object - properties: - workspace_id: - type: string - username: - type: string - required: - - workspace_id - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /users/decline_invite: - post: - summary: decline invite to workspace - operationId: declineInvite - tags: - - user - requestBody: - description: decline invite - required: true - content: - application/json: - schema: - type: object - properties: - workspace_id: - type: string - required: - - workspace_id - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/invite_user: - post: - summary: invite user to workspace - operationId: inviteUser - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceInvite - required: true - content: - application/json: - schema: - type: object - properties: - email: - type: string - is_admin: - type: boolean - operator: - type: boolean - required: - - email - - is_admin - - operator - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/add_user: - post: - summary: add user to workspace - operationId: addUser - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceInvite - required: true - content: - application/json: - schema: - type: object - properties: - email: - type: string - is_admin: - type: boolean - username: - type: string - operator: - type: boolean - required: - - email - - is_admin - - operator - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/delete_invite: - post: - summary: delete user invite - operationId: delete invite - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceInvite - required: true - content: - application/json: - schema: - type: object - properties: - email: - type: string - is_admin: - type: boolean - operator: - type: boolean - required: - - email - - is_admin - - operator - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/archive: - post: - summary: archive workspace - operationId: archiveWorkspace - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /workspaces/unarchive/{workspace}: - post: - summary: unarchive workspace - operationId: unarchiveWorkspace - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /workspaces/delete/{workspace}: - delete: - summary: delete workspace (require super admin) - operationId: deleteWorkspace - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/leave: - post: - summary: leave workspace - operationId: leaveWorkspace - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/get_workspace_name: - get: - summary: get workspace name - operationId: getWorkspaceName - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/change_workspace_name: - post: - summary: change workspace name - operationId: changeWorkspaceName - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - content: - application/json: - schema: - type: object - properties: - new_name: - type: string - required: - - username - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/change_workspace_id: - post: - summary: change workspace id - operationId: changeWorkspaceId - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - content: - application/json: - schema: - type: object - properties: - new_id: - type: string - new_name: - type: string - required: - - username - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/users/whois/{username}: - get: - summary: whois - operationId: whois - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: username - in: path - required: true - schema: - type: string - responses: - "200": - description: user - content: - application/json: - schema: - $ref: "#/components/schemas/User" - - /users/exists/{email}: - get: - summary: exists email - operationId: existsEmail - tags: - - user - parameters: - - name: email - in: path - required: true - schema: - type: string - responses: - "200": - description: user - content: - application/json: - schema: - type: boolean - - /users/list_as_super_admin: - get: - summary: list all users as super admin (require to be super amdin) - operationId: listUsersAsSuperAdmin - tags: - - user - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: active_only - in: query - description: filter only active users - schema: - type: boolean - responses: - "200": - description: user - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/GlobalUserInfo" - - /w/{workspace}/workspaces/list_pending_invites: - get: - summary: list pending invites for a workspace - operationId: listPendingInvites - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: user - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkspaceInvite" - - /w/{workspace}/workspaces/get_settings: - get: - summary: get settings - operationId: getSettings - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - workspace_id: - type: string - slack_name: - type: string - slack_team_id: - type: string - slack_command_script: - type: string - auto_invite_domain: - type: string - auto_invite_operator: - type: boolean - auto_add: - type: boolean - plan: - type: string - automatic_billing: - type: boolean - customer_id: - type: string - webhook: - type: string - deploy_to: - type: string - ai_resource: - $ref: "#/components/schemas/AiResource" - code_completion_enabled: - type: boolean - error_handler: - type: string - error_handler_extra_args: - $ref: "#/components/schemas/ScriptArgs" - error_handler_muted_on_cancel: - type: boolean - large_file_storage: - $ref: "#/components/schemas/LargeFileStorage" - git_sync: - $ref: "#/components/schemas/WorkspaceGitSyncSettings" - deploy_ui: - $ref: "#/components/schemas/WorkspaceDeployUISettings" - default_app: - type: string - default_scripts: - $ref: "#/components/schemas/WorkspaceDefaultScripts" - mute_critical_alerts: - type: boolean - required: - - code_completion_enabled - - automatic_billing - - error_handler_muted_on_cancel - - /w/{workspace}/workspaces/get_deploy_to: - get: - summary: get deploy to - operationId: getDeployTo - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - deploy_to: - type: string - - /w/{workspace}/workspaces/is_premium: - get: - summary: get if workspace is premium - operationId: getIsPremium - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - responses: - "200": - description: status - content: - application/json: - schema: - type: boolean - - /w/{workspace}/workspaces/premium_info: - get: - summary: get premium info - operationId: getPremiumInfo - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - premium: - type: boolean - usage: - type: number - seats: - type: number - automatic_billing: - type: boolean - required: - - premium - - automatic_billing - - /w/{workspace}/workspaces/set_automatic_billing: - post: - summary: set automatic billing - operationId: setAutomaticBilling - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: automatic billing - required: true - content: - application/json: - schema: - type: object - properties: - automatic_billing: - type: boolean - seats: - type: number - required: - - automatic_billing - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/edit_slack_command: - post: - summary: edit slack command - operationId: editSlackCommand - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceInvite - required: true - content: - application/json: - schema: - type: object - properties: - slack_command_script: - type: string - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/run_slack_message_test_job: - post: - summary: run a job that sends a message to Slack - operationId: runSlackMessageTestJob - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: path to hub script to run and its corresponding args - required: true - content: - application/json: - schema: - type: object - properties: - hub_script_path: - type: string - channel: - type: string - test_msg: - type: string - - responses: - "200": - description: status - content: - text/json: - schema: - type: object - properties: - job_uuid: - type: string - - /w/{workspace}/workspaces/edit_deploy_to: - post: - summary: edit deploy to - operationId: editDeployTo - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - deploy_to: - type: string - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/edit_auto_invite: - post: - summary: edit auto invite - operationId: editAutoInvite - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceInvite - required: true - content: - application/json: - schema: - type: object - properties: - operator: - type: boolean - invite_all: - type: boolean - auto_add: - type: boolean - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/edit_webhook: - post: - summary: edit webhook - operationId: editWebhook - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceWebhook - required: true - content: - application/json: - schema: - type: object - properties: - webhook: - type: string - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/edit_copilot_config: - post: - summary: edit copilot config - operationId: editCopilotConfig - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceCopilotConfig - required: true - content: - application/json: - schema: - type: object - required: - - code_completion_enabled - properties: - ai_resource: - $ref: "#/components/schemas/AiResource" - code_completion_enabled: - type: boolean - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/get_copilot_info: - get: - summary: get copilot info - operationId: getCopilotInfo - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - responses: - "200": - description: status - content: - text/plain: - schema: - type: object - properties: - ai_provider: - type: string - exists_ai_resource: - type: boolean - code_completion_enabled: - type: boolean - required: - - ai_provider - - exists_ai_resource - - code_completion_enabled - - /w/{workspace}/workspaces/edit_error_handler: - post: - summary: edit error handler - operationId: editErrorHandler - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: WorkspaceErrorHandler - required: true - content: - application/json: - schema: - type: object - properties: - error_handler: - type: string - error_handler_extra_args: - $ref: "#/components/schemas/ScriptArgs" - error_handler_muted_on_cancel: - type: boolean - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/edit_large_file_storage_config: - post: - summary: edit large file storage settings - operationId: editLargeFileStorageConfig - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: LargeFileStorage info - required: true - content: - application/json: - schema: - type: object - properties: - large_file_storage: - $ref: "#/components/schemas/LargeFileStorage" - - responses: - "200": - description: status - content: - application/json: - schema: {} - - /w/{workspace}/workspaces/edit_git_sync_config: - post: - summary: edit workspace git sync settings - operationId: editWorkspaceGitSyncConfig - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Workspace Git sync settings - required: true - content: - application/json: - schema: - type: object - properties: - git_sync_settings: - $ref: "#/components/schemas/WorkspaceGitSyncSettings" - - responses: - "200": - description: status - content: - application/json: - schema: {} - - /w/{workspace}/workspaces/edit_deploy_ui_config: - post: - summary: edit workspace deploy ui settings - operationId: editWorkspaceDeployUISettings - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Workspace deploy UI settings - required: true - content: - application/json: - schema: - type: object - properties: - deploy_ui_settings: - $ref: "#/components/schemas/WorkspaceDeployUISettings" - - responses: - "200": - description: status - content: - application/json: - schema: {} - - /w/{workspace}/workspaces/edit_default_app: - post: - summary: edit default app for workspace - operationId: editWorkspaceDefaultApp - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Workspace default app - required: true - content: - application/json: - schema: - type: object - properties: - default_app_path: - type: string - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/default_scripts: - post: - summary: edit default scripts for workspace - operationId: editDefaultScripts - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Workspace default app - content: - application/json: - schema: - $ref: "#/components/schemas/WorkspaceDefaultScripts" - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - get: - summary: get default scripts for workspace - operationId: get default scripts - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - application/json: - schema: - $ref: "#/components/schemas/WorkspaceDefaultScripts" - - /w/{workspace}/workspaces/set_environment_variable: - post: - summary: set environment variable - operationId: setEnvironmentVariable - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Workspace default app - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - value: - type: string - required: [name] - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/encryption_key: - get: - summary: retrieves the encryption key for this workspace - operationId: getWorkspaceEncryptionKey - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - key: - type: string - required: - - key - post: - summary: update the encryption key for this workspace - operationId: setWorkspaceEncryptionKey - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: New encryption key - required: true - content: - application/json: - schema: - type: object - properties: - new_key: - type: string - skip_reencrypt: - type: boolean - required: - - new_key - - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/default_app: - get: - summary: get default app for workspace - operationId: getWorkspaceDefaultApp - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - default_app_path: - type: string - - /w/{workspace}/workspaces/get_large_file_storage_config: - get: - summary: get large file storage config - operationId: getLargeFileStorageConfig - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - responses: - "200": - description: status - content: - application/json: - schema: - $ref: "#/components/schemas/LargeFileStorage" - - /w/{workspace}/workspaces/usage: - get: - summary: get usage - operationId: getWorkspaceUsage - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: usage - content: - text/plain: - schema: - type: number - - /w/{workspace}/workspaces/used_triggers: - get: - summary: get used triggers - operationId: getUsedTriggers - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: status - content: - application/json: - schema: - type: object - properties: - http_routes_used: - type: boolean - websocket_used: - type: boolean - required: - - http_routes_used - - websocket_used - - /w/{workspace}/users/list: - get: - summary: list users - operationId: listUsers - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: user - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User" - - /w/{workspace}/users/list_usage: - get: - summary: list users usage - operationId: listUsersUsage - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: user - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/UserUsage" - - /w/{workspace}/users/list_usernames: - get: - summary: list usernames - operationId: listUsernames - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: user - content: - application/json: - schema: - type: array - items: - type: string - - /w/{workspace}/users/username_to_email/{username}: - get: - summary: get email from username - operationId: usernameToEmail - tags: - - user - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: username - in: path - required: true - schema: - type: string - responses: - "200": - description: email - content: - text/plain: - schema: - type: string - - /users/tokens/create: - post: - summary: create token - operationId: createToken - tags: - - user - requestBody: - description: new token - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewToken" - responses: - "201": - description: token created - content: - text/plain: - schema: - type: string - - /users/tokens/impersonate: - post: - summary: create token to impersonate a user (require superadmin) - operationId: createTokenImpersonate - tags: - - user - requestBody: - description: new token - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewTokenImpersonate" - responses: - "201": - description: token created - content: - text/plain: - schema: - type: string - - /users/tokens/delete/{token_prefix}: - delete: - summary: delete token - operationId: deleteToken - tags: - - user - parameters: - - name: token_prefix - in: path - required: true - schema: - type: string - responses: - "200": - description: delete token - content: - text/plain: - schema: - type: string - - /users/tokens/list: - get: - summary: list token - operationId: listTokens - tags: - - user - parameters: - - name: exclude_ephemeral - in: query - schema: - type: boolean - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: truncated token - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/TruncatedToken" - - /w/{workspace}/oidc/token/{audience}: - post: - summary: get OIDC token (ee only) - operationId: getOidcToken - tags: - - oidc - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: audience - in: path - required: true - schema: - type: string - - responses: - "200": - description: new oidc token - content: - text/plain: - schema: - type: string - - /w/{workspace}/variables/create: - post: - summary: create variable - operationId: createVariable - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: already_encrypted - in: query - schema: - type: boolean - requestBody: - description: new variable - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateVariable" - responses: - "201": - description: variable created - content: - text/plain: - schema: - type: string - - /w/{workspace}/variables/encrypt: - post: - summary: encrypt value - operationId: encryptValue - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new variable - required: true - content: - application/json: - schema: - type: string - responses: - "200": - description: encrypted value - content: - text/plain: - schema: - type: string - - /w/{workspace}/variables/delete/{path}: - delete: - summary: delete variable - operationId: deleteVariable - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: variable deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/variables/update/{path}: - post: - summary: update variable - operationId: updateVariable - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: already_encrypted - in: query - schema: - type: boolean - requestBody: - description: updated variable - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditVariable" - responses: - "200": - description: variable updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/variables/get/{path}: - get: - summary: get variable - operationId: getVariable - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: decrypt_secret - description: | - ask to decrypt secret if this variable is secret - (if not secret no effect, default: true) - in: query - schema: - type: boolean - - name: include_encrypted - description: | - ask to include the encrypted value if secret and decrypt secret is not true (default: false) - in: query - schema: - type: boolean - responses: - "200": - description: variable - content: - application/json: - schema: - $ref: "#/components/schemas/ListableVariable" - - /w/{workspace}/variables/get_value/{path}: - get: - summary: get variable value - operationId: getVariableValue - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: variable - content: - application/json: - schema: - type: string - - /w/{workspace}/variables/exists/{path}: - get: - summary: does variable exists at path - operationId: existsVariable - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: variable - content: - application/json: - schema: - type: boolean - - /w/{workspace}/variables/list: - get: - summary: list variables - operationId: listVariable - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: path_start - in: query - schema: - type: string - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: variable list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ListableVariable" - - /w/{workspace}/variables/list_contextual: - get: - summary: list contextual variables - operationId: listContextualVariables - tags: - - variable - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: contextual variable list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ContextualVariable" - - /w/{workspace}/workspaces/critical_alerts: - get: - summary: Get all critical alerts for this workspace - operationId: workspaceGetCriticalAlerts - tags: - - setting - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - in: query - name: page - schema: - type: integer - default: 1 - description: The page number to retrieve (minimum value is 1) - - in: query - name: page_size - schema: - type: integer - default: 10 - maximum: 100 - description: Number of alerts per page (maximum is 100) - - in: query - name: acknowledged - schema: - type: boolean - nullable: true - description: Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts - responses: - "200": - description: Successfully retrieved all critical alerts - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/CriticalAlert' - - /w/{workspace}/workspaces/critical_alerts/{id}/acknowledge: - post: - summary: Acknowledge a critical alert for this workspace - operationId: workspaceAcknowledgeCriticalAlert - tags: - - setting - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - in: path - name: id - required: true - schema: - type: integer - description: The ID of the critical alert to acknowledge - responses: - "200": - description: Successfully acknowledged the critical alert - content: - application/json: - schema: - type: string - example: "Critical alert acknowledged" - - /w/{workspace}/workspaces/critical_alerts/acknowledge_all: - post: - summary: Acknowledge all unacknowledged critical alerts for this workspace - operationId: workspaceAcknowledgeAllCriticalAlerts - tags: - - setting - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: Successfully acknowledged all unacknowledged critical alerts. - content: - application/json: - schema: - type: string - example: "All unacknowledged critical alerts acknowledged" - - /w/{workspace}/workspaces/critical_alerts/mute: - post: - summary: Mute critical alert UI for this workspace - operationId: workspaceMuteCriticalAlertsUI - tags: - - setting - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Boolean flag to mute critical alerts. - required: true - content: - application/json: - schema: - type: object - properties: - mute_critical_alerts: - type: boolean - description: Whether critical alerts should be muted. - example: true - responses: - '200': - description: Successfully updated mute critical alert settings. - content: - application/json: - schema: - type: string - example: "Updated mute critical alert UI settings for workspace: workspace_id" - - /oauth/login_callback/{client_name}: - post: - security: [] - summary: login with oauth authorization flow - operationId: loginWithOauth - tags: - - user - parameters: - - $ref: "#/components/parameters/ClientName" - requestBody: - description: Partially filled script - required: true - content: - application/json: - schema: - type: object - properties: - code: - type: string - state: - type: string - - responses: - "200": - description: > - Successfully authenticated. The session ID is returned in a cookie - named `token` and as plaintext response. Preferred method of - authorization is through the bearer token. The cookie is only for - browser convenience. - - headers: - Set-Cookie: - schema: - type: string - example: token=abcde12345; Path=/; HttpOnly - content: - text/plain: - schema: - type: string - - /w/{workspace}/oauth/connect_slack_callback: - post: - summary: connect slack callback - operationId: connectSlackCallback - tags: - - oauth - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: code endpoint - required: true - content: - application/json: - schema: - type: object - properties: - code: - type: string - state: - type: string - required: - - code - - state - responses: - "200": - description: slack token - content: - text/plain: - schema: - type: string - - - /oauth/connect_slack_callback: - post: - summary: connect slack callback instance - operationId: connectSlackCallbackInstance - tags: - - oauth - requestBody: - description: code endpoint - required: true - content: - application/json: - schema: - type: object - properties: - code: - type: string - state: - type: string - required: - - code - - state - responses: - "200": - description: success message - content: - text/plain: - schema: - type: string - - /oauth/connect_callback/{client_name}: - post: - summary: connect callback - operationId: connectCallback - tags: - - oauth - parameters: - - $ref: "#/components/parameters/ClientName" - requestBody: - description: code endpoint - required: true - content: - application/json: - schema: - type: object - properties: - code: - type: string - state: - type: string - required: - - code - - state - responses: - "200": - description: oauth token - content: - application/json: - schema: - $ref: "#/components/schemas/TokenResponse" - - /w/{workspace}/oauth/create_account: - post: - summary: create OAuth account - operationId: createAccount - tags: - - oauth - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: code endpoint - required: true - content: - application/json: - schema: - type: object - properties: - refresh_token: - type: string - expires_in: - type: integer - client: - type: string - required: - - expires_in - - client - responses: - "200": - description: account set - content: - text/plain: - schema: - type: string - - /w/{workspace}/oauth/refresh_token/{id}: - post: - summary: refresh token - operationId: refreshToken - tags: - - oauth - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/AccountId" - requestBody: - description: variable path - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - required: - - path - responses: - "200": - description: token refreshed - content: - text/plain: - schema: - type: string - - /w/{workspace}/oauth/disconnect/{id}: - post: - summary: disconnect account - operationId: disconnectAccount - tags: - - oauth - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/AccountId" - responses: - "200": - description: disconnected client - content: - text/plain: - schema: - type: string - - /w/{workspace}/oauth/disconnect_slack: - post: - summary: disconnect slack - operationId: disconnectSlack - tags: - - oauth - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: disconnected slack - content: - text/plain: - schema: - type: string - - /oauth/list_logins: - get: - summary: list oauth logins - operationId: listOAuthLogins - tags: - - oauth - responses: - "200": - description: list of oauth and saml login clients - content: - application/json: - schema: - type: object - properties: - oauth: - type: array - items: - type: object - properties: - type: - type: string - display_name: - type: string - required: - - type - saml: - type: string - required: - - oauth - - /oauth/list_connects: - get: - summary: list oauth connects - operationId: listOAuthConnects - tags: - - oauth - responses: - "200": - description: list of oauth connects clients - content: - application/json: - schema: - type: array - items: - type: string - - /oauth/get_connect/{client}: - get: - summary: get oauth connect - operationId: getOAuthConnect - tags: - - oauth - parameters: - - name: client - description: client name - in: path - required: true - schema: - type: string - responses: - "200": - description: get - content: - application/json: - schema: - type: object - properties: - extra_params: - type: object - scopes: - type: array - items: - type: string - - - - - /w/{workspace}/resources/create: - post: - summary: create resource - operationId: createResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: update_if_exists - in: query - schema: - type: boolean - requestBody: - description: new resource - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateResource" - responses: - "201": - description: resource created - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/delete/{path}: - delete: - summary: delete resource - operationId: deleteResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: resource deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/update/{path}: - post: - summary: update resource - operationId: updateResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated resource - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditResource" - responses: - "200": - description: resource updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/update_value/{path}: - post: - summary: update resource value - operationId: updateResourceValue - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated resource - required: true - content: - application/json: - schema: - type: object - properties: - value: {} - responses: - "200": - description: resource value updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/get/{path}: - get: - summary: get resource - operationId: getResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: resource - content: - application/json: - schema: - $ref: "#/components/schemas/Resource" - - /w/{workspace}/resources/get_value_interpolated/{path}: - get: - summary: get resource interpolated (variables and resources are fully unrolled) - operationId: getResourceValueInterpolated - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: job_id - description: job id - in: query - schema: - type: string - format: uuid - responses: - "200": - description: resource value - content: - application/json: - schema: {} - - /w/{workspace}/resources/get_value/{path}: - get: - summary: get resource value - operationId: getResourceValue - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: resource value - content: - application/json: - schema: {} - - /w/{workspace}/resources/exists/{path}: - get: - summary: does resource exists - operationId: existsResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: does resource exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/resources/list: - get: - summary: list resources - operationId: listResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: resource_type - description: resource_types to list from, separated by ',', - in: query - schema: - type: string - - name: resource_type_exclude - description: resource_types to not list from, separated by ',', - in: query - schema: - type: string - - name: path_start - in: query - schema: - type: string - responses: - "200": - description: resource list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ListableResource" - - /w/{workspace}/resources/list_search: - get: - summary: list resources for search - operationId: listSearchResource - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: resource list - content: - application/json: - schema: - type: array - items: - type: object - properties: - path: - type: string - value: {} - required: - - path - - value - - /w/{workspace}/resources/list_names/{name}: - get: - summary: list resource names - operationId: listResourceNames - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - responses: - "200": - description: resource list names - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - path: - type: string - required: - - name - - path - - /w/{workspace}/resources/type/create: - post: - summary: create resource_type - operationId: createResourceType - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new resource_type - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ResourceType" - responses: - "201": - description: resource_type created - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/file_resource_type_to_file_ext_map: - get: - summary: get map from resource type to format extension - operationId: fileResourceTypeToFileExtMap - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: map from resource type to file ext - content: - application/json: - schema: {} - - - /w/{workspace}/resources/type/delete/{path}: - delete: - summary: delete resource_type - operationId: deleteResourceType - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: resource_type deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/type/update/{path}: - post: - summary: update resource_type - operationId: updateResourceType - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated resource_type - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditResourceType" - responses: - "200": - description: resource_type updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/resources/type/get/{path}: - get: - summary: get resource_type - operationId: getResourceType - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: resource_type deleted - content: - application/json: - schema: - $ref: "#/components/schemas/ResourceType" - - /w/{workspace}/resources/type/exists/{path}: - get: - summary: does resource_type exists - operationId: existsResourceType - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: does resource_type exist - content: - application/json: - schema: - type: boolean - - /w/{workspace}/resources/type/list: - get: - summary: list resource_types - operationId: listResourceType - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: resource_type list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ResourceType" - - /w/{workspace}/resources/type/listnames: - get: - summary: list resource_types names - operationId: listResourceTypeNames - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: resource_type list - content: - application/json: - schema: - type: array - items: - type: string - - /w/{workspace}/embeddings/query_resource_types: - get: - summary: query resource types by similarity - operationId: queryResourceTypes - tags: - - resource - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: text - description: query text - in: query - required: true - schema: - type: string - - name: limit - description: query limit - in: query - required: false - schema: - type: number - responses: - "200": - description: resource type details - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - score: - type: number - schema: {} - required: - - name - - score - - /integrations/hub/list: - get: - summary: list hub integrations - operationId: listHubIntegrations - tags: - - integration - parameters: - - name: kind - description: query integrations kind - in: query - required: false - schema: - type: string - responses: - "200": - description: integrations details - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - required: - - name - - /flows/hub/list: - get: - summary: list all hub flows - operationId: listHubFlows - tags: - - flow - responses: - "200": - description: hub flows list - content: - application/json: - schema: - type: object - properties: - flows: - type: array - items: - type: object - properties: - id: - type: number - flow_id: - type: number - summary: - type: string - apps: - type: array - items: - type: string - approved: - type: boolean - votes: - type: number - - required: - - id - - flow_id - - summary - - apps - - approved - - votes - - /flows/hub/get/{id}: - get: - summary: get hub flow by id - operationId: getHubFlowById - tags: - - flow - parameters: - - $ref: "#/components/parameters/PathId" - responses: - "200": - description: flow - content: - application/json: - schema: - type: object - properties: - flow: - $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" - - /apps/hub/list: - get: - summary: list all hub apps - operationId: listHubApps - tags: - - app - responses: - "200": - description: hub apps list - content: - application/json: - schema: - type: object - properties: - apps: - type: array - items: - type: object - properties: - id: - type: number - app_id: - type: number - summary: - type: string - apps: - type: array - items: - type: string - approved: - type: boolean - votes: - type: number - required: - - id - - app_id - - summary - - apps - - approved - - votes - - /apps/hub/get/{id}: - get: - summary: get hub app by id - operationId: getHubAppById - tags: - - app - parameters: - - $ref: "#/components/parameters/PathId" - responses: - "200": - description: app - content: - application/json: - schema: - type: object - properties: - app: - type: object - properties: - summary: - type: string - value: {} - required: - - summary - - value - required: - - app - - /scripts/hub/get/{path}: - get: - summary: get hub script content by path - operationId: getHubScriptContentByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script details - content: - text/plain: - schema: - type: string - - /scripts/hub/get_full/{path}: - get: - summary: get full hub script by path - operationId: getHubScriptByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script details - content: - application/json: - schema: - type: object - properties: - content: - type: string - lockfile: - type: string - schema: {} - language: - type: string - summary: - type: string - required: - - content - - language - - /scripts/hub/top: - get: - summary: get top hub scripts - operationId: getTopHubScripts - tags: - - script - parameters: - - name: limit - description: query limit - in: query - required: false - schema: - type: number - - name: app - description: query scripts app - in: query - required: false - schema: - type: string - - name: kind - description: query scripts kind - in: query - required: false - schema: - type: string - responses: - "200": - description: hub scripts list - content: - application/json: - schema: - type: object - properties: - asks: - type: array - items: - type: object - properties: - id: - type: number - ask_id: - type: number - summary: - type: string - app: - type: string - version_id: - type: number - kind: - $ref: "#/components/schemas/HubScriptKind" - votes: - type: number - views: - type: number - required: - - id - - ask_id - - summary - - app - - version_id - - kind - - views - - votes - - /embeddings/query_hub_scripts: - get: - summary: query hub scripts by similarity - operationId: queryHubScripts - tags: - - script - parameters: - - name: text - description: query text - in: query - required: true - schema: - type: string - - name: kind - description: query scripts kind - in: query - required: false - schema: - type: string - - name: limit - description: query limit - in: query - required: false - schema: - type: number - - name: app - description: query scripts app - in: query - required: false - schema: - type: string - responses: - "200": - description: script details - content: - application/json: - schema: - type: array - items: - type: object - properties: - ask_id: - type: number - id: - type: number - version_id: - type: number - summary: - type: string - app: - type: string - kind: - $ref: "#/components/schemas/HubScriptKind" - score: - type: number - required: - - ask_id - - id - - version_id - - summary - - app - - kind - - score - - /w/{workspace}/scripts/list_search: - get: - summary: list scripts for search - operationId: listSearchScript - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: script list - content: - application/json: - schema: - type: array - items: - type: object - properties: - path: - type: string - content: - type: string - required: - - path - - content - - /w/{workspace}/scripts/list: - get: - summary: list all scripts - operationId: listScripts - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - name: path_start - description: mask to filter matching starting path - in: query - schema: - type: string - - name: path_exact - description: mask to filter exact matching path - in: query - schema: - type: string - - name: first_parent_hash - description: mask to filter scripts whom first direct parent has exact hash - in: query - schema: - type: string - - name: last_parent_hash - description: | - mask to filter scripts whom last parent in the chain has exact hash. - Beware that each script stores only a limited number of parents. Hence - the last parent hash for a script is not necessarily its top-most parent. - To find the top-most parent you will have to jump from last to last hash - until finding the parent - in: query - schema: - type: string - - name: parent_hash - description: | - is the hash present in the array of stored parent hashes for this script. - The same warning applies than for last_parent_hash. A script only store a - limited number of direct parent - in: query - schema: - type: string - - name: show_archived - description: | - (default false) - show only the archived files. - when multiple archived hash share the same path, only the ones with the latest create_at - are - ed. - in: query - schema: - type: boolean - - name: include_without_main - description: | - (default false) - include scripts without an exported main function - in: query - schema: - type: boolean - - name: include_draft_only - description: | - (default false) - include scripts that have no deployed version - in: query - schema: - type: boolean - - name: is_template - description: | - (default regardless) - if true show only the templates - if false show only the non templates - if not defined, show all regardless of if the script is a template - in: query - schema: - type: boolean - - name: kinds - description: | - (default regardless) - script kinds to filter, split by comma - in: query - schema: - type: string - - name: starred_only - description: | - (default false) - show only the starred items - in: query - schema: - type: boolean - - name: with_deployment_msg - description: | - (default false) - include deployment message - in: query - schema: - type: boolean - responses: - "200": - description: All scripts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Script" - - /w/{workspace}/scripts/list_paths: - get: - summary: list all scripts paths - operationId: listScriptPaths - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: list of script paths - content: - text/plain: - schema: - type: array - items: - type: string - - /w/{workspace}/drafts/create: - post: - summary: create draft - operationId: createDraft - tags: - - draft - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - typ: - type: string - enum: ["flow", "script", "app"] - value: {} - required: - - path - - typ - - enum - responses: - "201": - description: draft created - content: - text/plain: - schema: - type: string - - /w/{workspace}/drafts/delete/{kind}/{path}: - delete: - summary: delete draft - operationId: deleteDraft - tags: - - draft - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: kind - in: path - required: true - schema: - type: string - enum: - - script - - flow - - app - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: draft deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/scripts/create: - post: - summary: create script - operationId: createScript - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Partially filled script - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewScript" - - responses: - "201": - description: script created - content: - text/plain: - schema: - type: string - - /w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}: - post: - summary: Toggle ON and OFF the workspace error handler for a given script - operationId: toggleWorkspaceErrorHandlerForScript - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: Workspace error handler enabled - required: true - content: - application/json: - schema: - type: object - properties: - muted: - type: boolean - responses: - "200": - description: error handler toggled - content: - text/plain: - schema: - type: string - - /workers/custom_tags: - get: - summary: get all instance custom tags (tags are used to dispatch jobs to - different worker groups) - operationId: getCustomTags - tags: - - worker - responses: - "200": - description: list of custom tags - content: - application/json: - schema: - type: array - items: - type: string - - /workers/get_default_tags: - get: - summary: get all instance default tags - operationId: geDefaultTags - tags: - - worker - responses: - "200": - description: list of default tags - content: - application/json: - schema: - type: array - items: - type: string - - /workers/is_default_tags_per_workspace: - get: - summary: is default tags per workspace - operationId: isDefaultTagsPerWorkspace - tags: - - worker - responses: - "200": - description: is the default tags per workspace - content: - application/json: - schema: - type: boolean - - /w/{workspace}/scripts/archive/p/{path}: - post: - summary: archive script by path - operationId: archiveScriptByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script archived - content: - text/plain: - schema: - type: string - - /w/{workspace}/scripts/archive/h/{hash}: - post: - summary: archive script by hash - operationId: archiveScriptByHash - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptHash" - responses: - "200": - description: script details - content: - application/json: - schema: - $ref: "#/components/schemas/Script" - - /w/{workspace}/scripts/delete/h/{hash}: - post: - summary: delete script by hash (erase content but keep hash, require admin) - operationId: deleteScriptByHash - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptHash" - responses: - "200": - description: script details - content: - application/json: - schema: - $ref: "#/components/schemas/Script" - - /w/{workspace}/scripts/delete/p/{path}: - post: - summary: delete all scripts at a given path (require admin) - operationId: deleteScriptByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script path - content: - application/json: - schema: - type: string - - /w/{workspace}/scripts/get/p/{path}: - get: - summary: get script by path - operationId: getScriptByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - name: with_starred_info - in: query - schema: - type: boolean - responses: - "200": - description: script details - content: - application/json: - schema: - $ref: "#/components/schemas/Script" - - /w/{workspace}/scripts/get_triggers_count/{path}: - get: - summary: get triggers count of script - operationId: getTriggersCountOfScript - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: triggers count - content: - application/json: - schema: - $ref: "#/components/schemas/TriggersCount" - - /w/{workspace}/scripts/list_tokens/{path}: - get: - summary: get tokens with script scope - operationId: listTokensOfScript - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: tokens list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/TruncatedToken" - - /w/{workspace}/scripts/get/draft/{path}: - get: - summary: get script by path with draft - operationId: getScriptByPathWithDraft - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script details - content: - application/json: - schema: - $ref: "#/components/schemas/NewScriptWithDraft" - - /w/{workspace}/scripts/history/p/{path}: - get: - summary: get history of a script by path - operationId: getScriptHistoryByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script history - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ScriptHistory" - - /w/{workspace}/scripts/get_latest_version/{path}: - get: - summary: get scripts's latest version (hash) - operationId: getScriptLatestVersion - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - tags: - - script - responses: - "200": - description: Script version/hash - content: - application/json: - - required: false - - schema: - $ref: "#/components/schemas/ScriptHistory" - - /w/{workspace}/scripts/history_update/h/{hash}/p/{path}: - post: - summary: update history of a script - operationId: updateScriptHistory - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptHash" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: Script deployment message - required: true - content: - application/json: - schema: - type: object - properties: - deployment_msg: - type: string - responses: - "200": - description: success - content: - text/plain: - schema: - type: string - - /w/{workspace}/scripts/raw/p/{path}: - get: - summary: raw script by path - operationId: rawScriptByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script content - content: - text/plain: - schema: - type: string - - /scripts_u/tokened_raw/{workspace}/{token}/{path}: - get: - summary: - raw script by path with a token (mostly used by lsp to be used with - import maps to resolve scripts) - operationId: rawScriptByPathTokened - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Token" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script content - content: - text/plain: - schema: - type: string - - /w/{workspace}/scripts/exists/p/{path}: - get: - summary: exists script by path - operationId: existsScriptByPath - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: does it exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/scripts/get/h/{hash}: - get: - summary: get script by hash - operationId: getScriptByHash - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptHash" - - name: with_starred_info - in: query - schema: - type: boolean - responses: - "200": - description: script details - content: - application/json: - schema: - $ref: "#/components/schemas/Script" - - /w/{workspace}/scripts/raw/h/{path}: - get: - summary: raw script by hash - operationId: rawScriptByHash - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: script content - content: - text/plain: - schema: - type: string - - /w/{workspace}/scripts/deployment_status/h/{hash}: - get: - summary: get script deployment status - operationId: getScriptDeploymentStatus - tags: - - script - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptHash" - responses: - "200": - description: script details - content: - application/json: - schema: - type: object - properties: - lock: - type: string - lock_error_logs: - type: string - - /w/{workspace}/jobs/run/p/{path}: - post: - summary: run script by path - operationId: runScriptByPath - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - name: scheduled_for - description: when to schedule this job (leave empty for immediate run) - in: query - schema: - type: string - format: date-time - - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now - in: query - schema: - type: integer - - name: skip_preprocessor - description: skip the preprocessor - in: query - schema: - type: boolean - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/WorkerTag" - - $ref: "#/components/parameters/CacheTtl" - - $ref: "#/components/parameters/NewJobId" - - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) - in: query - schema: - type: boolean - requestBody: - description: script args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/openai_sync/p/{path}: - post: - summary: run script by path in openai format - operationId: openaiSyncScriptByPath - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/NewJobId" - - $ref: "#/components/parameters/IncludeHeader" - - $ref: "#/components/parameters/QueueLimit" - - requestBody: - description: script args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "200": - description: job result - content: - application/json: - schema: {} - - /w/{workspace}/jobs/run_wait_result/p/{path}: - post: - summary: run script by path - operationId: runWaitResultScriptByPath - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/WorkerTag" - - $ref: "#/components/parameters/CacheTtl" - - $ref: "#/components/parameters/NewJobId" - - $ref: "#/components/parameters/IncludeHeader" - - $ref: "#/components/parameters/QueueLimit" - - requestBody: - description: script args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "200": - description: job result - content: - application/json: - schema: {} - - get: - summary: run script by path with get - operationId: runWaitResultScriptByPathGet - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/WorkerTag" - - $ref: "#/components/parameters/CacheTtl" - - $ref: "#/components/parameters/NewJobId" - - $ref: "#/components/parameters/IncludeHeader" - - $ref: "#/components/parameters/QueueLimit" - - $ref: "#/components/parameters/Payload" - - responses: - "200": - description: job result - content: - application/json: - schema: {} - - /w/{workspace}/jobs/openai_sync/f/{path}: - post: - summary: run flow by path and wait until completion in openai format - operationId: openaiSyncFlowByPath - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - $ref: "#/components/parameters/IncludeHeader" - - $ref: "#/components/parameters/QueueLimit" - - $ref: "#/components/parameters/NewJobId" - - requestBody: - description: script args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "200": - description: job result - content: - application/json: - schema: {} - - /w/{workspace}/jobs/run_wait_result/f/{path}: - post: - summary: run flow by path and wait until completion - operationId: runWaitResultFlowByPath - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - $ref: "#/components/parameters/IncludeHeader" - - $ref: "#/components/parameters/QueueLimit" - - $ref: "#/components/parameters/NewJobId" - - requestBody: - description: script args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "200": - description: job result - content: - application/json: - schema: {} - - /w/{workspace}/jobs/result_by_id/{flow_job_id}/{node_id}: - get: - summary: get job result by id - operationId: resultById - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: flow_job_id - in: path - required: true - schema: - type: string - - name: node_id - in: path - required: true - schema: - type: string - responses: - "200": - description: job result - content: - application/json: - schema: {} - - /w/{workspace}/flows/list_paths: - get: - summary: list all flow paths - operationId: listFlowPaths - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: list of flow paths - content: - text/plain: - schema: - type: array - items: - type: string - - /w/{workspace}/flows/list_search: - get: - summary: list flows for search - operationId: listSearchFlow - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: flow list - content: - application/json: - schema: - type: array - items: - type: object - properties: - path: - type: string - value: {} - required: - - path - - value - - /w/{workspace}/flows/list: - get: - summary: list all flows - operationId: listFlows - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - name: path_start - description: mask to filter matching starting path - in: query - schema: - type: string - - name: path_exact - description: mask to filter exact matching path - in: query - schema: - type: string - - name: show_archived - description: | - (default false) - show only the archived files. - when multiple archived hash share the same path, only the ones with the latest create_at - are displayed. - in: query - schema: - type: boolean - - name: starred_only - description: | - (default false) - show only the starred items - in: query - schema: - type: boolean - - name: include_draft_only - description: | - (default false) - include items that have no deployed version - in: query - schema: - type: boolean - - name: with_deployment_msg - description: | - (default false) - include deployment message - in: query - schema: - type: boolean - responses: - "200": - description: All flow - content: - application/json: - schema: - type: array - items: - allOf: - - $ref: "#/components/schemas/Flow" - - type: object - properties: - has_draft: - type: boolean - draft_only: - type: boolean - - /w/{workspace}/flows/history/p/{path}: - get: - summary: get flow history by path - operationId: getFlowHistory - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - tags: - - flow - responses: - "200": - description: Flow history - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/FlowVersion" - - /w/{workspace}/flows/get_latest_version/{path}: - get: - summary: get flow's latest version - operationId: getFlowLatestVersion - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - tags: - - flow - responses: - "200": - description: Flow version - content: - application/json: - required: false - - schema: - $ref: "#/components/schemas/FlowVersion" - - /w/{workspace}/flows/get/v/{version}/p/{path}: - get: - summary: get flow version - operationId: getFlowVersion - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - type: string - name: version - in: path - required: true - schema: - type: number - - $ref: "#/components/parameters/ScriptPath" - tags: - - flow - responses: - "200": - description: flow details - content: - application/json: - schema: - $ref: "#/components/schemas/Flow" - - /w/{workspace}/flows/history_update/v/{version}/p/{path}: - post: - summary: update flow history - operationId: updateFlowHistory - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - type: string - name: version - in: path - required: true - schema: - type: number - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: Flow deployment message - required: true - content: - application/json: - schema: - type: object - properties: - deployment_msg: - type: string - required: - - deployment_msg - tags: - - flow - responses: - "200": - description: success - content: - text/plain: - schema: - type: string - - - /w/{workspace}/flows/get/{path}: - get: - summary: get flow by path - operationId: getFlowByPath - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - name: with_starred_info - in: query - schema: - type: boolean - responses: - "200": - description: flow details - content: - application/json: - schema: - $ref: "#/components/schemas/Flow" - - /w/{workspace}/flows/get_triggers_count/{path}: - get: - summary: get triggers count of flow - operationId: getTriggersCountOfFlow - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: triggers count - content: - application/json: - schema: - $ref: "#/components/schemas/TriggersCount" - - /w/{workspace}/flows/list_tokens/{path}: - get: - summary: get tokens with flow scope - operationId: listTokensOfFlow - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: tokens list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/TruncatedToken" - - - /w/{workspace}/flows/toggle_workspace_error_handler/{path}: - post: - summary: Toggle ON and OFF the workspace error handler for a given flow - operationId: toggleWorkspaceErrorHandlerForFlow - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: Workspace error handler enabled - required: true - content: - application/json: - schema: - type: object - properties: - muted: - type: boolean - responses: - "200": - description: error handler toggled - content: - text/plain: - schema: - type: string - - /w/{workspace}/flows/get/draft/{path}: - get: - summary: get flow by path with draft - operationId: getFlowByPathWithDraft - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: flow details with draft - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/Flow" - - type: object - properties: - draft: - $ref: "#/components/schemas/Flow" - - /w/{workspace}/flows/exists/{path}: - get: - summary: exists flow by path - operationId: existsFlowByPath - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: flow details - content: - application/json: - schema: - type: boolean - - /w/{workspace}/flows/create: - post: - summary: create flow - operationId: createFlow - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Partially filled flow - required: true - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/OpenFlowWPath" - - type: object - properties: - draft_only: - type: boolean - deployment_message: - type: string - responses: - "201": - description: flow created - content: - text/plain: - schema: - type: string - - /w/{workspace}/flows/update/{path}: - post: - summary: update flow - operationId: updateFlow - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: Partially filled flow - required: true - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/OpenFlowWPath" - - type: object - properties: - deployment_message: - type: string - - responses: - "200": - description: flow updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/flows/archive/{path}: - post: - summary: archive flow by path - operationId: archiveFlowByPath - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: archiveFlow - required: true - content: - application/json: - schema: - type: object - properties: - archived: - type: boolean - responses: - "200": - description: flow archived - content: - text/plain: - schema: - type: string - - /w/{workspace}/flows/delete/{path}: - delete: - summary: delete flow by path - operationId: deleteFlowByPath - tags: - - flow - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: flow delete - content: - text/plain: - schema: - type: string - - - /w/{workspace}/raw_apps/list: - get: - summary: list all raw apps - operationId: listRawApps - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - name: path_start - description: mask to filter matching starting path - in: query - schema: - type: string - - name: path_exact - description: mask to filter exact matching path - in: query - schema: - type: string - - name: starred_only - description: | - (default false) - show only the starred items - in: query - schema: - type: boolean - responses: - "200": - description: All raw apps - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ListableRawApp" - - /w/{workspace}/raw_apps/exists/{path}: - get: - summary: does an app exisst at path - operationId: existsRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/apps/get_data/{version}/{path}: - get: - summary: get app by path - operationId: getRawAppData - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/VersionId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: app details - content: - text/javascript: - schema: - type: string - - /w/{workspace}/apps/list_search: - get: - summary: list apps for search - operationId: listSearchApp - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: app list - content: - application/json: - schema: - type: array - items: - type: object - properties: - path: - type: string - value: {} - required: - - path - - value - - /w/{workspace}/apps/list: - get: - summary: list all apps - operationId: listApps - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - name: path_start - description: mask to filter matching starting path - in: query - schema: - type: string - - name: path_exact - description: mask to filter exact matching path - in: query - schema: - type: string - - name: starred_only - description: | - (default false) - show only the starred items - in: query - schema: - type: boolean - - name: include_draft_only - description: | - (default false) - include items that have no deployed version - in: query - schema: - type: boolean - - name: with_deployment_msg - description: | - (default false) - include deployment message - in: query - schema: - type: boolean - responses: - "200": - description: All apps - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ListableApp" - - /w/{workspace}/apps/create: - post: - summary: create app - operationId: createApp - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new app - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - value: {} - summary: - type: string - policy: - $ref: "#/components/schemas/Policy" - draft_only: - type: boolean - deployment_message: - type: string - required: - - path - - value - - summary - - policy - responses: - "201": - description: app created - content: - text/plain: - schema: - type: string - - /w/{workspace}/apps/exists/{path}: - get: - summary: does an app exisst at path - operationId: existsApp - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/apps/get/p/{path}: - get: - summary: get app by path - operationId: getAppByPath - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - name: with_starred_info - in: query - schema: - type: boolean - responses: - "200": - description: app details - content: - application/json: - schema: - $ref: "#/components/schemas/AppWithLastVersion" - - /w/{workspace}/apps/get/draft/{path}: - get: - summary: get app by path with draft - operationId: getAppByPathWithDraft - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: app details with draft - content: - application/json: - schema: - $ref: "#/components/schemas/AppWithLastVersionWDraft" - - /w/{workspace}/apps/history/p/{path}: - get: - summary: get app history by path - operationId: getAppHistoryByPath - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - responses: - "200": - description: app history - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/AppHistory" - - /w/{workspace}/apps/get_latest_version/{path}: - get: - summary: get apps's latest version - operationId: getAppLatestVersion - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - tags: - - app - responses: - "200": - description: App version - content: - application/json: - required: false - schema: - $ref: "#/components/schemas/AppHistory" - - /w/{workspace}/apps/history_update/a/{id}/v/{version}: - post: - summary: update app history - operationId: updateAppHistory - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/PathId" - - $ref: "#/components/parameters/PathVersion" - requestBody: - description: App deployment message - required: true - content: - application/json: - schema: - type: object - properties: - deployment_msg: - type: string - responses: - "200": - description: success - content: - text/plain: - schema: - type: string - - /w/{workspace}/apps_u/public_app/{path}: - get: - summary: get public app by secret - operationId: getPublicAppBySecret - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app details - content: - application/json: - schema: - $ref: "#/components/schemas/AppWithLastVersion" - - /w/{workspace}/apps_u/public_resource/{path}: - get: - summary: get public resource - operationId: get public resource - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: resource value - content: - application/json: - schema: {} - - /w/{workspace}/apps/secret_of/{path}: - get: - summary: get public secret of app - operationId: getPublicSecretOfApp - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app secret - content: - text/plain: - schema: - type: string - - /w/{workspace}/apps/get/v/{id}: - get: - summary: get app by version - operationId: getAppByVersion - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/PathId" - responses: - "200": - description: app details - content: - application/json: - schema: - $ref: "#/components/schemas/AppWithLastVersion" - - /w/{workspace}/raw_apps/create: - post: - summary: create raw app - operationId: createRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new raw app - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - value: - type: string - summary: - type: string - required: - - path - - value - - summary - responses: - "201": - description: raw app created - content: - text/plain: - schema: - type: string - - /w/{workspace}/raw_apps/update/{path}: - post: - summary: update app - operationId: updateRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: updateraw app - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - summary: - type: string - value: - type: string - responses: - "200": - description: app updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/raw_apps/delete/{path}: - delete: - summary: delete raw app - operationId: deleteRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/apps/delete/{path}: - delete: - summary: delete app - operationId: deleteApp - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/apps/update/{path}: - post: - summary: update app - operationId: updateApp - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: update app - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - summary: - type: string - value: {} - policy: - $ref: "#/components/schemas/Policy" - deployment_message: - type: string - responses: - "200": - description: app updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/apps_u/execute_component/{path}: - post: - summary: executeComponent - operationId: executeComponent - tags: - - app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: update app - required: true - content: - application/json: - schema: - type: object - properties: - component: - type: string - #script: script/ - #flow: flow/ - path: - type: string - args: {} - raw_code: - type: object - properties: - content: - type: string - language: - type: string - path: - type: string - lock: - type: string - cache_ttl: - type: integer - required: - - content - - language - force_viewer_static_fields: - type: object - force_viewer_one_of_fields: - type: object - force_viewer_allow_user_resources: - type: array - items: - type: string - required: - - args - - component - - responses: - "200": - description: job uuid - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs/run/f/{path}: - post: - summary: run flow by path - operationId: runFlowByPath - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - - name: scheduled_for - description: when to schedule this job (leave empty for immediate run) - in: query - schema: - type: string - format: date-time - - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now - in: query - schema: - type: integer - - name: skip_preprocessor - description: skip the preprocessor - in: query - schema: - type: boolean - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/WorkerTag" - - $ref: "#/components/parameters/NewJobId" - - $ref: "#/components/parameters/IncludeHeader" - - name: invisible_to_owner - description: make the run invisible to the the flow owner (default false) - in: query - schema: - type: boolean - - requestBody: - description: flow args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}: - post: - summary: restart a completed flow at a given step - operationId: restartFlowAtStep - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: step_id - description: step id to restart the flow from - required: true - in: path - schema: - type: string - - name: branch_or_iteration_n - description: - for branchall or loop, the iteration at which the flow should - restart - required: true - in: path - schema: - type: integer - - name: scheduled_for - description: when to schedule this job (leave empty for immediate run) - in: query - schema: - type: string - format: date-time - - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now - in: query - schema: - type: integer - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/WorkerTag" - - $ref: "#/components/parameters/NewJobId" - - $ref: "#/components/parameters/IncludeHeader" - - name: invisible_to_owner - description: make the run invisible to the the flow owner (default false) - in: query - schema: - type: boolean - - requestBody: - description: flow args - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ScriptArgs" - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/run/h/{hash}: - post: - summary: run script by hash - operationId: runScriptByHash - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptHash" - - name: scheduled_for - description: when to schedule this job (leave empty for immediate run) - in: query - schema: - type: string - format: date-time - - name: scheduled_in_secs - description: schedule the script to execute in the number of seconds starting now - in: query - schema: - type: integer - - name: skip_preprocessor - description: skip the preprocessor - in: query - schema: - type: boolean - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/WorkerTag" - - $ref: "#/components/parameters/CacheTtl" - - $ref: "#/components/parameters/NewJobId" - - $ref: "#/components/parameters/IncludeHeader" - - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) - in: query - schema: - type: boolean - requestBody: - description: Partially filled args - required: true - content: - application/json: - schema: - type: object - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/run/preview: - post: - summary: run script preview - operationId: runScriptPreview - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/IncludeHeader" - - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) - in: query - schema: - type: boolean - - $ref: "#/components/parameters/NewJobId" - - requestBody: - description: preview - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/Preview" - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/workflow_as_code/{job_id}/{entrypoint}: - post: - summary: run code-workflow task - operationId: runCodeWorkflowTask - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - - name: job_id - in: path - required: true - schema: - type: string - - name: entrypoint - in: path - required: true - schema: - type: string - - requestBody: - description: preview - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowTask" - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/run/dependencies: - post: - summary: run a one-off dependencies job - operationId: runRawScriptDependencies - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - requestBody: - description: raw script content - required: true - content: - application/json: - schema: - type: object - properties: - raw_scripts: - type: array - items: - $ref: "#/components/schemas/RawScriptForDependencies" - entrypoint: - type: string - required: - - entrypoint - - raw_scripts - responses: - "201": - description: dependency job result - content: - application/json: - schema: - type: object - properties: - lock: - type: string - required: - - lock - - /w/{workspace}/jobs/run/preview_flow: - post: - summary: run flow preview - operationId: runFlowPreview - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/IncludeHeader" - - name: invisible_to_owner - description: make the run invisible to the the script owner (default false) - in: query - schema: - type: boolean - - $ref: "#/components/parameters/NewJobId" - - requestBody: - description: preview - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/FlowPreview" - - responses: - "201": - description: job created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/jobs/queue/list: - get: - summary: list all queued jobs - operationId: listQueue - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/ScriptExactPath" - - $ref: "#/components/parameters/ScriptStartPath" - - $ref: "#/components/parameters/SchedulePath" - - $ref: "#/components/parameters/ScriptExactHash" - - $ref: "#/components/parameters/StartedBefore" - - $ref: "#/components/parameters/StartedAfter" - - $ref: "#/components/parameters/Success" - - $ref: "#/components/parameters/ScheduledForBeforeNow" - - $ref: "#/components/parameters/JobKinds" - - $ref: "#/components/parameters/Suspended" - - $ref: "#/components/parameters/Running" - - $ref: "#/components/parameters/ArgsFilter" - - $ref: "#/components/parameters/ResultFilter" - - $ref: "#/components/parameters/Tag" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: all_workspaces - description: get jobs from all workspaces (only valid if request come from the `admins` workspace) - in: query - schema: - type: boolean - - name: is_not_schedule - description: is not a scheduled job - in: query - schema: - type: boolean - responses: - "200": - description: All queued jobs - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/QueuedJob" - - /w/{workspace}/jobs/queue/count: - get: - summary: get queue count - operationId: getQueueCount - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: all_workspaces - description: get jobs from all workspaces (only valid if request come from the `admins` workspace) - in: query - schema: - type: boolean - responses: - "200": - description: queue count - content: - application/json: - schema: - type: object - properties: - database_length: - type: integer - suspended: - type: integer - required: - - database_length - - /w/{workspace}/jobs/completed/count: - get: - summary: get completed count - operationId: getCompletedCount - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - responses: - "200": - description: completed count - content: - application/json: - schema: - type: object - properties: - database_length: - type: integer - required: - - database_length - - /w/{workspace}/jobs/completed/count_jobs: - get: - summary: count number of completed jobs with filter - operationId: countCompletedJobs - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: completed_after_s_ago - in: query - schema: - type: integer - - name: success - in: query - schema: - type: boolean - - name: tags - in: query - schema: - type: string - - name: all_workspaces - in: query - schema: - type: boolean - responses: - "200": - description: Count of completed jobs - content: - application/json: - schema: - type: integer - - - /w/{workspace}/jobs/queue/list_filtered_uuids: - get: - summary: get the ids of all jobs matching the given filters - operationId: listFilteredUuids - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/ScriptExactPath" - - $ref: "#/components/parameters/ScriptStartPath" - - $ref: "#/components/parameters/SchedulePath" - - $ref: "#/components/parameters/ScriptExactHash" - - $ref: "#/components/parameters/StartedBefore" - - $ref: "#/components/parameters/StartedAfter" - - $ref: "#/components/parameters/Success" - - $ref: "#/components/parameters/ScheduledForBeforeNow" - - $ref: "#/components/parameters/JobKinds" - - $ref: "#/components/parameters/Suspended" - - $ref: "#/components/parameters/Running" - - $ref: "#/components/parameters/ArgsFilter" - - $ref: "#/components/parameters/ResultFilter" - - $ref: "#/components/parameters/Tag" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: concurrency_key - in: query - required: false - schema: - type: string - - name: all_workspaces - description: get jobs from all workspaces (only valid if request come from the `admins` workspace) - in: query - schema: - type: boolean - - name: is_not_schedule - description: is not a scheduled job - in: query - schema: - type: boolean - responses: - "200": - description: uuids of jobs - content: - application/json: - schema: - type: array - items: - type: string - - /w/{workspace}/jobs/queue/cancel_selection: - post: - summary: cancel jobs based on the given uuids - operationId: cancelSelection - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: uuids of the jobs to cancel - required: true - content: - application/json: - schema: - type: array - items: - type: string - responses: - "200": - description: uuids of canceled jobs - content: - application/json: - schema: - type: array - items: - type: string - - /w/{workspace}/jobs/completed/list: - get: - summary: list all completed jobs - operationId: listCompletedJobs - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/OrderDesc" - - $ref: "#/components/parameters/CreatedBy" - - $ref: "#/components/parameters/Label" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/ScriptExactPath" - - $ref: "#/components/parameters/ScriptStartPath" - - $ref: "#/components/parameters/SchedulePath" - - $ref: "#/components/parameters/ScriptExactHash" - - $ref: "#/components/parameters/StartedBefore" - - $ref: "#/components/parameters/StartedAfter" - - $ref: "#/components/parameters/Success" - - $ref: "#/components/parameters/JobKinds" - - $ref: "#/components/parameters/ArgsFilter" - - $ref: "#/components/parameters/ResultFilter" - - $ref: "#/components/parameters/Tag" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: is_skipped - description: is the job skipped - in: query - schema: - type: boolean - - name: is_flow_step - description: is the job a flow step - in: query - schema: - type: boolean - - name: has_null_parent - description: has null parent - in: query - schema: - type: boolean - - name: is_not_schedule - description: is not a scheduled job - in: query - schema: - type: boolean - responses: - "200": - description: All completed jobs - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/CompletedJob" - - /w/{workspace}/jobs/list: - get: - summary: list all jobs - operationId: listJobs - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/CreatedBy" - - $ref: "#/components/parameters/Label" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/ScriptExactPath" - - $ref: "#/components/parameters/ScriptStartPath" - - $ref: "#/components/parameters/SchedulePath" - - $ref: "#/components/parameters/ScriptExactHash" - - $ref: "#/components/parameters/StartedBefore" - - $ref: "#/components/parameters/StartedAfter" - - $ref: "#/components/parameters/CreatedBefore" - - $ref: "#/components/parameters/CreatedAfter" - - $ref: "#/components/parameters/CreatedOrStartedBefore" - - $ref: "#/components/parameters/Running" - - $ref: "#/components/parameters/ScheduledForBeforeNow" - - $ref: "#/components/parameters/CreatedOrStartedAfter" - - $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob" - - $ref: "#/components/parameters/JobKinds" - - $ref: "#/components/parameters/Suspended" - - $ref: "#/components/parameters/ArgsFilter" - - $ref: "#/components/parameters/Tag" - - $ref: "#/components/parameters/ResultFilter" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: is_skipped - description: is the job skipped - in: query - schema: - type: boolean - - name: is_flow_step - description: is the job a flow step - in: query - schema: - type: boolean - - name: has_null_parent - description: has null parent - in: query - schema: - type: boolean - - name: success - description: filter on successful jobs - in: query - schema: - type: boolean - - name: all_workspaces - description: get jobs from all workspaces (only valid if request come from the `admins` workspace) - in: query - schema: - type: boolean - - name: is_not_schedule - description: is not a scheduled job - in: query - schema: - type: boolean - responses: - "200": - description: All jobs - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Job" - - /jobs/db_clock: - get: - summary: get db clock - operationId: getDbClock - tags: - - job - responses: - "200": - description: the timestamp of the db that can be used to compute the drift - content: - application/json: - schema: - type: integer - - /jobs/completed/count_by_tag: - get: - summary: Count jobs by tag - operationId: countJobsByTag - tags: - - job - parameters: - - name: horizon_secs - in: query - description: Past Time horizon in seconds (when to start the count = now - horizon) (default is 3600) - required: false - schema: - type: integer - - name: workspace_id - in: query - description: Specific workspace ID to filter results (optional) - required: false - schema: - type: string - responses: - "200": - description: Job counts by tag - content: - application/json: - schema: - type: array - items: - type: object - properties: - tag: - type: string - count: - type: integer - required: - - tag - - count - - /w/{workspace}/jobs_u/get/{id}: - get: - summary: get job - operationId: getJob - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: no_logs - in: query - schema: - type: boolean - responses: - "200": - description: job details - content: - application/json: - schema: - $ref: "#/components/schemas/Job" - - /w/{workspace}/jobs_u/get_root_job_id/{id}: - get: - summary: get root job id - operationId: getRootJobId - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: get root job id - content: - application/json: - schema: - type: string - - /w/{workspace}/jobs_u/get_logs/{id}: - get: - summary: get job logs - operationId: getJob logs - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: job details - content: - text/plain: - schema: - type: string - - - /w/{workspace}/jobs_u/get_args/{id}: - get: - summary: get job args - operationId: getJobArgs - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: job args - content: - application/json: - schema: {} - - /w/{workspace}/jobs_u/getupdate/{id}: - get: - summary: get job updates - operationId: getJobUpdates - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: running - in: query - schema: - type: boolean - - name: log_offset - in: query - schema: - type: integer - - name: get_progress - in: query - schema: - type: boolean - - responses: - "200": - description: job details - content: - application/json: - schema: - type: object - properties: - running: - type: boolean - completed: - type: boolean - new_logs: - type: string - log_offset: - type: integer - mem_peak: - type: integer - progress: - type: integer - flow_status: - $ref: "#/components/schemas/WorkflowStatusRecord" - - /w/{workspace}/jobs_u/get_log_file/{path}: - get: - summary: get log file from object store - operationId: getLogFileFromStore - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: path - in: path - required: true - schema: - type: string - responses: - "200": - description: job log - content: - text/plain: - type: string - - /w/{workspace}/jobs_u/get_flow_debug_info/{id}: - get: - summary: get flow debug info - operationId: getFlowDebugInfo - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: flow debug info details - content: - application/json: - schema: {} - - /w/{workspace}/jobs_u/completed/get/{id}: - get: - summary: get completed job - operationId: getCompletedJob - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: job details - content: - application/json: - schema: - $ref: "#/components/schemas/CompletedJob" - - /w/{workspace}/jobs_u/completed/get_result/{id}: - get: - summary: get completed job result - operationId: getCompletedJobResult - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: suspended_job - in: query - schema: - type: string - - name: resume_id - in: query - schema: - type: integer - - name: secret - in: query - schema: - type: string - - name: approver - in: query - schema: - type: string - responses: - "200": - description: result - content: - application/json: - schema: {} - - /w/{workspace}/jobs_u/completed/get_result_maybe/{id}: - get: - summary: get completed job result if job is completed - operationId: getCompletedJobResultMaybe - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - $ref: "#/components/parameters/GetStarted" - - responses: - "200": - description: result - content: - application/json: - schema: - type: object - properties: - completed: - type: boolean - result: {} - success: - type: boolean - started: - type: boolean - required: - - completed - - result - - /w/{workspace}/jobs/completed/delete/{id}: - post: - summary: delete completed job (erase content but keep run id) - operationId: deleteCompletedJob - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: job details - content: - application/json: - schema: - $ref: "#/components/schemas/CompletedJob" - - /w/{workspace}/jobs_u/queue/cancel/{id}: - post: - summary: cancel queued or running job - operationId: cancelQueuedJob - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - requestBody: - description: reason - required: true - content: - application/json: - schema: - type: object - properties: - reason: - type: string - - responses: - "200": - description: job canceled - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs_u/queue/cancel_persistent/{path}: - post: - summary: cancel all queued jobs for persistent script - operationId: cancelPersistentQueuedJobs - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: reason - required: true - content: - application/json: - schema: - type: object - properties: - reason: - type: string - - responses: - "200": - description: persistent job scaled down to zero - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs_u/queue/force_cancel/{id}: - post: - summary: force cancel queued job - operationId: forceCancelQueuedJob - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - requestBody: - description: reason - required: true - content: - application/json: - schema: - type: object - properties: - reason: - type: string - - responses: - "200": - description: job canceled - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs/job_signature/{id}/{resume_id}: - get: - summary: create an HMac signature given a job id and a resume id - operationId: createJobSignature - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: approver - in: query - schema: - type: string - responses: - "200": - description: job signature - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs/resume_urls/{id}/{resume_id}: - get: - summary: get resume urls given a job_id, resume_id and a nonce to resume a flow - operationId: getResumeUrls - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: approver - in: query - schema: - type: string - responses: - "200": - description: url endpoints - content: - application/json: - schema: - type: object - properties: - approvalPage: - type: string - resume: - type: string - cancel: - type: string - required: - - approvalPage - - resume - - cancel - - /w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}: - get: - summary: resume a job for a suspended flow - operationId: resumeSuspendedJobGet - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - $ref: "#/components/parameters/Payload" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: signature - in: path - required: true - schema: - type: string - - name: approver - in: query - schema: - type: string - responses: - "201": - description: job resumed - content: - text/plain: - schema: - type: string - - post: - summary: resume a job for a suspended flow - operationId: resumeSuspendedJobPost - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: signature - in: path - required: true - schema: - type: string - - name: approver - in: query - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - responses: - "201": - description: job resumed - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs/flow/user_states/{id}/{key}: - post: - summary: set flow user state at a given key - operationId: setFlowUserState - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: key - in: path - required: true - schema: - type: string - requestBody: - description: new value - required: true - content: - application/json: - schema: {} - responses: - "200": - description: flow user state updated - content: - text/plain: - schema: - type: string - get: - summary: get flow user state at a given key - operationId: getFlowUserState - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: key - in: path - required: true - schema: - type: string - responses: - "200": - description: flow user state updated - content: - application/json: - schema: {} - - /w/{workspace}/jobs/flow/resume/{id}: - post: - summary: resume a job for a suspended flow as an owner - operationId: resumeSuspendedFlowAsOwner - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - requestBody: - required: true - content: - application/json: - schema: - type: object - responses: - "201": - description: job resumed - content: - text/plain: - schema: - type: string - - - /w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}: - get: - summary: cancel a job for a suspended flow - operationId: cancelSuspendedJobGet - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: signature - in: path - required: true - schema: - type: string - - name: approver - in: query - schema: - type: string - responses: - "201": - description: job canceled - content: - text/plain: - schema: - type: string - - post: - summary: cancel a job for a suspended flow - operationId: cancelSuspendedJobPost - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: signature - in: path - required: true - schema: - type: string - - name: approver - in: query - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - responses: - "201": - description: job canceled - content: - text/plain: - schema: - type: string - - /w/{workspace}/jobs_u/get_flow/{id}/{resume_id}/{signature}: - get: - summary: get parent flow job of suspended job - operationId: getSuspendedJobFlow - tags: - - job - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - - name: resume_id - in: path - required: true - schema: - type: integer - - name: signature - in: path - required: true - schema: - type: string - - name: approver - in: query - schema: - type: string - responses: - "200": - description: parent flow details - content: - application/json: - schema: - type: object - properties: - job: - $ref: "#/components/schemas/Job" - approvers: - type: array - items: - type: object - properties: - resume_id: - type: integer - approver: - type: string - required: - - resume_id - - approver - required: - - job - - approvers - - /schedules/preview: - post: - summary: preview schedule - operationId: previewSchedule - tags: - - schedule - requestBody: - description: schedule - required: true - content: - application/json: - schema: - type: object - properties: - schedule: - type: string - timezone: - type: string - required: - - schedule - - timezone - responses: - "200": - description: List of 5 estimated upcoming execution events (in UTC) - content: - application/json: - schema: - type: array - items: - type: string - format: date-time - - /w/{workspace}/schedules/create: - post: - summary: create schedule - operationId: createSchedule - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new schedule - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewSchedule" - responses: - "201": - description: schedule created - content: - text/plain: - schema: - type: string - - /w/{workspace}/schedules/update/{path}: - post: - summary: update schedule - operationId: updateSchedule - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated schedule - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditSchedule" - responses: - "200": - description: schedule updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/schedules/setenabled/{path}: - post: - summary: set enabled schedule - operationId: setScheduleEnabled - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated schedule enable - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - required: - - enabled - - responses: - "200": - description: schedule enabled set - content: - text/plain: - schema: - type: string - - /w/{workspace}/schedules/delete/{path}: - delete: - summary: delete schedule - operationId: deleteSchedule - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: schedule deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/schedules/get/{path}: - get: - summary: get schedule - operationId: getSchedule - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: schedule deleted - content: - application/json: - schema: - $ref: "#/components/schemas/Schedule" - - /w/{workspace}/schedules/exists/{path}: - get: - summary: does schedule exists - operationId: existsSchedule - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: schedule exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/schedules/list: - get: - summary: list schedules - operationId: listSchedules - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - $ref: "#/components/parameters/ArgsFilter" - - name: path - description: filter by path - in: query - schema: - type: string - - name: is_flow - in: query - schema: - type: boolean - - name: path_start - in: query - schema: - type: string - responses: - "200": - description: schedule list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Schedule" - - /w/{workspace}/schedules/list_with_jobs: - get: - summary: list schedules with last 20 jobs - operationId: listSchedulesWithJobs - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: schedule list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ScheduleWJobs" - - /w/{workspace}/schedules/setdefaulthandler: - post: - summary: Set default error or recoevery handler - operationId: setDefaultErrorOrRecoveryHandler - tags: - - schedule - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: Handler description - required: true - content: - application/json: - schema: - type: object - properties: - handler_type: - type: string - enum: ["error", "recovery", "success"] - override_existing: - type: boolean - path: - type: string - extra_args: - type: object - number_of_occurence: - type: integer - number_of_occurence_exact: - type: boolean - workspace_handler_muted: - type: boolean - required: - - handler_type - - override_existing - responses: - "201": - description: default error handler set - - - /w/{workspace}/http_triggers/create: - post: - summary: create http trigger - operationId: createHttpTrigger - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new http trigger - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewHttpTrigger" - responses: - "201": - description: http trigger created - content: - text/plain: - schema: - type: string - - /w/{workspace}/http_triggers/update/{path}: - post: - summary: update http trigger - operationId: updateHttpTrigger - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated trigger - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditHttpTrigger" - responses: - "200": - description: http trigger updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/http_triggers/delete/{path}: - delete: - summary: delete http trigger - operationId: deleteHttpTrigger - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: http trigger deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/http_triggers/get/{path}: - get: - summary: get http trigger - operationId: getHttpTrigger - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: http trigger deleted - content: - application/json: - schema: - $ref: "#/components/schemas/HttpTrigger" - - - /w/{workspace}/http_triggers/list: - get: - summary: list http triggers - operationId: listHttpTriggers - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - required: true - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: path - description: filter by path - in: query - schema: - type: string - - name: is_flow - in: query - schema: - type: boolean - - name: path_start - in: query - schema: - type: string - responses: - "200": - description: http trigger list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/HttpTrigger" - - - /w/{workspace}/http_triggers/exists/{path}: - get: - summary: does http trigger exists - operationId: existsHttpTrigger - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: http trigger exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/http_triggers/route_exists: - post: - summary: does route exists - operationId: existsRoute - tags: - - http_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: route exists request - required: true - content: - application/json: - schema: - type: object - properties: - route_path: - type: string - http_method: - type: string - enum: ["get", "post", "put", "delete", "patch"] - required: - - kind - - route_path - - http_method - responses: - "200": - description: route exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/websocket_triggers/create: - post: - summary: create websocket trigger - operationId: createWebsocketTrigger - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new websocket trigger - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewWebsocketTrigger" - responses: - "201": - description: websocket trigger created - content: - text/plain: - schema: - type: string - - /w/{workspace}/websocket_triggers/update/{path}: - post: - summary: update websocket trigger - operationId: updateWebsocketTrigger - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated trigger - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditWebsocketTrigger" - responses: - "200": - description: websocket trigger updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/websocket_triggers/delete/{path}: - delete: - summary: delete websocket trigger - operationId: deleteWebsocketTrigger - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: websocket trigger deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/websocket_triggers/get/{path}: - get: - summary: get websocket trigger - operationId: getWebsocketTrigger - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: websocket trigger deleted - content: - application/json: - schema: - $ref: "#/components/schemas/WebsocketTrigger" - - - /w/{workspace}/websocket_triggers/list: - get: - summary: list websocket triggers - operationId: listWebsocketTriggers - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - required: true - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: path - description: filter by path - in: query - schema: - type: string - - name: is_flow - in: query - schema: - type: boolean - - name: path_start - in: query - schema: - type: string - responses: - "200": - description: websocket trigger list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WebsocketTrigger" - - - /w/{workspace}/websocket_triggers/exists/{path}: - get: - summary: does websocket trigger exists - operationId: existsWebsocketTrigger - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: websocket trigger exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/websocket_triggers/setenabled/{path}: - post: - summary: set enabled websocket trigger - operationId: setWebsocketTriggerEnabled - tags: - - websocket_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated websocket trigger enable - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - required: - - enabled - responses: - "200": - description: websocket trigger enabled set - content: - text/plain: - schema: - type: string - - /w/{workspace}/database_triggers/create: - post: - summary: create database trigger - operationId: createDatabaseTrigger - tags: - - database_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new database trigger - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewDatabaseTrigger" - responses: - "201": - description: database trigger created - content: - text/plain: - schema: - type: string - - /w/{workspace}/database_triggers/update/{path}: - post: - summary: update database trigger - operationId: updateDatabaseTrigger - tags: - - database_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated trigger - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EditDatabaseTrigger" - responses: - "200": - description: database trigger updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/database_triggers/delete/{path}: - delete: - summary: delete database trigger - operationId: deleteDatabaseTrigger - tags: - - database_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: database trigger deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/database_triggers/list: - get: - summary: list database triggers - operationId: listDatabaseTriggers - tags: - - database_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - required: true - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: path - description: filter by path - in: query - schema: - type: string - - name: is_flow - in: query - schema: - type: boolean - - name: path_start - in: query - schema: - type: string - responses: - "200": - description: database trigger list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/DatabaseTrigger" - - /w/{workspace}/database_triggers/exists/{path}: - get: - summary: does database trigger exists - operationId: existsDatabaseTrigger - tags: - - database_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: database trigger exists - content: - application/json: - schema: - type: boolean - - /w/{workspace}/database_triggers/setenabled/{path}: - post: - summary: set enabled database trigger - operationId: setDatabaseTriggerEnabled - tags: - - database_trigger - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - requestBody: - description: updated database trigger enable - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - required: - - enabled - responses: - "200": - description: database trigger enabled set - content: - text/plain: - schema: - type: string - - /groups/list: - get: - summary: list instance groups - operationId: listInstanceGroups - tags: - - group - responses: - "200": - description: instance group list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/InstanceGroup" - - /groups/get/{name}: - get: - summary: get instance group - operationId: getInstanceGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/Name" - responses: - "200": - description: instance group - content: - application/json: - schema: - $ref: "#/components/schemas/InstanceGroup" - - /groups/create: - post: - summary: create instance group - operationId: createInstanceGroup - tags: - - group - requestBody: - description: create instance group - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - summary: - type: string - required: - - name - responses: - "200": - description: instance group created - content: - text/plain: - schema: - type: string - - /groups/update/{name}: - post: - summary: update instance group - operationId: updateInstanceGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/Name" - requestBody: - description: update instance group - required: true - content: - application/json: - schema: - type: object - properties: - new_summary: - type: string - required: - - new_summary - responses: - "200": - description: instance group updated - content: - text/plain: - schema: - type: string - - /groups/delete/{name}: - delete: - summary: delete instance group - operationId: deleteInstanceGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/Name" - responses: - "200": - description: instance group deleted - content: - text/plain: - schema: - type: string - - /groups/adduser/{name}: - post: - summary: add user to instance group - operationId: addUserToInstanceGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/Name" - requestBody: - description: user to add to instance group - required: true - content: - application/json: - schema: - type: object - properties: - email: - type: string - required: - - email - responses: - "200": - description: user added to instance group - content: - text/plain: - schema: - type: string - - /groups/removeuser/{name}: - post: - summary: remove user from instance group - operationId: removeUserFromInstanceGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/Name" - requestBody: - description: user to remove from instance group - required: true - content: - application/json: - schema: - type: object - properties: - email: - type: string - required: - - email - responses: - "200": - description: user removed from instance group - content: - text/plain: - schema: - type: string - /groups/export: - get: - summary: export instance groups - operationId: exportInstanceGroups - tags: - - group - responses: - "200": - description: exported instance groups - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ExportedInstanceGroup" - - /groups/overwrite: - post: - summary: overwrite instance groups - operationId: overwriteInstanceGroups - tags: - - group - requestBody: - description: overwrite instance groups - required: true - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ExportedInstanceGroup" - responses: - "200": - description: success message - content: - text/plain: - schema: - type: string - - /w/{workspace}/groups/list: - get: - summary: list groups - operationId: listGroups - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: group list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Group" - - /w/{workspace}/groups/listnames: - get: - summary: list group names - operationId: listGroupNames - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: only_member_of - in: query - description: only list the groups the user is member of (default false) - schema: - type: boolean - responses: - "200": - description: group list - content: - application/json: - schema: - type: array - items: - type: string - - /w/{workspace}/groups/create: - post: - summary: create group - operationId: createGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: create group - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - summary: - type: string - required: - - name - responses: - "200": - description: group created - content: - text/plain: - schema: - type: string - - /w/{workspace}/groups/update/{name}: - post: - summary: update group - operationId: updateGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - requestBody: - description: updated group - required: true - content: - application/json: - schema: - type: object - properties: - summary: - type: string - responses: - "200": - description: group updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/groups/delete/{name}: - delete: - summary: delete group - operationId: deleteGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - responses: - "200": - description: group deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/groups/get/{name}: - get: - summary: get group - operationId: getGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - responses: - "200": - description: group - content: - application/json: - schema: - $ref: "#/components/schemas/Group" - - /w/{workspace}/groups/adduser/{name}: - post: - summary: add user to group - operationId: addUserToGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - requestBody: - description: added user to group - required: true - content: - application/json: - schema: - type: object - properties: - username: - type: string - responses: - "200": - description: user added to group - content: - text/plain: - schema: - type: string - - /w/{workspace}/groups/removeuser/{name}: - post: - summary: remove user to group - operationId: removeUserToGroup - tags: - - group - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - requestBody: - description: added user to group - required: true - content: - application/json: - schema: - type: object - properties: - username: - type: string - responses: - "200": - description: user removed from group - content: - text/plain: - schema: - type: string - - /w/{workspace}/folders/list: - get: - summary: list folders - operationId: listFolders - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: folder list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Folder" - - /w/{workspace}/folders/listnames: - get: - summary: list folder names - operationId: listFolderNames - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: only_member_of - in: query - description: only list the folders the user is member of (default false) - schema: - type: boolean - responses: - "200": - description: folder list - content: - application/json: - schema: - type: array - items: - type: string - - /w/{workspace}/folders/create: - post: - summary: create folder - operationId: createFolder - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: create folder - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - summary: - type: string - owners: - type: array - items: - type: string - extra_perms: - additionalProperties: - type: boolean - required: - - name - responses: - "200": - description: folder created - content: - text/plain: - schema: - type: string - - /w/{workspace}/folders/update/{name}: - post: - summary: update folder - operationId: updateFolder - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - requestBody: - description: update folder - required: true - content: - application/json: - schema: - type: object - properties: - summary: - type: string - owners: - type: array - items: - type: string - extra_perms: - additionalProperties: - type: boolean - responses: - "200": - description: folder updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/folders/delete/{name}: - delete: - summary: delete folder - operationId: deleteFolder - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - responses: - "200": - description: folder deleted - content: - text/plain: - schema: - type: string - - /w/{workspace}/folders/get/{name}: - get: - summary: get folder - operationId: getFolder - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - responses: - "200": - description: folder - content: - application/json: - schema: - $ref: "#/components/schemas/Folder" - - /w/{workspace}/folders/getusage/{name}: - get: - summary: get folder usage - operationId: getFolderUsage - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - responses: - "200": - description: folder - content: - application/json: - schema: - type: object - properties: - scripts: - type: number - flows: - type: number - apps: - type: number - resources: - type: number - variables: - type: number - schedules: - type: number - required: - - scripts - - flows - - apps - - resources - - variables - - schedules - - /w/{workspace}/folders/addowner/{name}: - post: - summary: add owner to folder - operationId: addOwnerToFolder - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - requestBody: - description: owner user to folder - required: true - content: - application/json: - schema: - type: object - properties: - owner: - type: string - required: - - owner - - responses: - "200": - description: owner added to folder - content: - text/plain: - schema: - type: string - - /w/{workspace}/folders/removeowner/{name}: - post: - summary: remove owner to folder - operationId: removeOwnerToFolder - tags: - - folder - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Name" - requestBody: - description: added owner to folder - required: true - content: - application/json: - schema: - type: object - properties: - owner: - type: string - write: - type: boolean - required: - - owner - responses: - "200": - description: owner removed from folder - content: - text/plain: - schema: - type: string - - /workers/list: - get: - summary: list workers - operationId: listWorkers - tags: - - worker - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: ping_since - in: query - required: false - description: number of seconds the worker must have had a last ping more recent of (default to 300) - schema: - type: integer - responses: - "200": - description: a list of workers - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkerPing" - - /workers/exists_worker_with_tag: - get: - summary: exists worker with tag - operationId: existsWorkerWithTag - tags: - - worker - parameters: - - name: tag - in: query - required: true - schema: - type: string - responses: - "200": - description: whether a worker with the tag exists - content: - application/json: - schema: - type: boolean - - /workers/queue_metrics: - get: - summary: get queue metrics - operationId: getQueueMetrics - tags: - - worker - responses: - "200": - description: metrics - content: - application/json: - schema: - type: array - items: - type: object - properties: - id: - type: string - values: - type: array - items: - type: object - properties: - created_at: - type: string - value: - type: number - required: - - created_at - - value - required: - - id - - values - - /workers/queue_counts: - get: - summary: get counts of jobs waiting for an executor per tag - operationId: getCountsOfJobsWaitingPerTag - tags: - - worker - responses: - "200": - description: queue counts - content: - application/json: - schema: - type: object - additionalProperties: - type: integer - - - /configs/list_worker_groups: - get: - summary: list worker groups - operationId: listWorkerGroups - tags: - - config - responses: - "200": - description: a list of worker group configs - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - config: {} - required: - - name - - config - - /configs/get/{name}: - get: - summary: get config - operationId: get config - tags: - - config - parameters: - - $ref: "#/components/parameters/Name" - responses: - "200": - description: a config - content: - application/json: - schema: {} - - /configs/update/{name}: - post: - summary: Update config - operationId: updateConfig - tags: - - config - parameters: - - $ref: "#/components/parameters/Name" - requestBody: - description: worker group - required: true - content: - application/json: - schema: {} - responses: - "200": - description: Update a worker group - content: - text/plain: - schema: - type: string - delete: - summary: Delete Config - operationId: deleteConfig - tags: - - config - parameters: - - $ref: "#/components/parameters/Name" - responses: - "200": - description: Delete config - content: - text/plain: - schema: - type: string - - /configs/list: - get: - summary: list configs - operationId: listConfigs - tags: - - config - responses: - "200": - description: list of configs - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Config" - - /configs/list_autoscaling_events/{worker_group}: - get: - summary: List autoscaling events - operationId: listAutoscalingEvents - tags: - - config - parameters: - - name: worker_group - in: path - required: true - schema: - type: string - responses: - "200": - description: List of autoscaling events - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/AutoscalingEvent" - - - /w/{workspace}/acls/get/{kind}/{path}: - get: - summary: get granular acls - operationId: getGranularAcls - tags: - - granular_acl - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: kind - in: path - required: true - schema: - type: string - enum: - [ - script, - group_, - resource, - schedule, - variable, - flow, - folder, - app, - raw_app, - http_trigger, - websocket_trigger, - database_trigger - ] - responses: - "200": - description: acls - content: - application/json: - schema: - type: object - additionalProperties: - type: boolean - - /w/{workspace}/acls/add/{kind}/{path}: - post: - summary: add granular acls - operationId: addGranularAcls - tags: - - granular_acl - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: kind - in: path - required: true - schema: - type: string - enum: - [ - script, - group_, - resource, - schedule, - variable, - flow, - folder, - app, - raw_app, - http_trigger, - websocket_trigger, - database_trigger - ] - requestBody: - description: acl to add - required: true - content: - application/json: - schema: - type: object - properties: - owner: - type: string - write: - type: boolean - required: [owner] - responses: - "200": - description: granular acl added - content: - text/plain: - schema: - type: string - - /w/{workspace}/acls/remove/{kind}/{path}: - post: - summary: remove granular acls - operationId: removeGranularAcls - tags: - - granular_acl - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: kind - in: path - required: true - schema: - type: string - enum: - [ - script, - group_, - resource, - schedule, - variable, - flow, - folder, - app, - raw_app, - http_trigger, - websocket_trigger, - database_trigger - ] - requestBody: - description: acl to add - required: true - content: - application/json: - schema: - type: object - properties: - owner: - type: string - required: [owner] - responses: - "200": - description: granular acl removed - content: - text/plain: - schema: - type: string - - /w/{workspace}/capture_u/{path}: - post: - summary: update flow preview capture - operationId: updateCapture - tags: - - capture - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "204": - description: flow preview captured - - /w/{workspace}/capture/{path}: - put: - summary: create flow preview capture - operationId: createCapture - tags: - - capture - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "201": - description: flow preview capture created - get: - summary: get flow preview capture - operationId: getCapture - tags: - - capture - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: captured flow preview - content: - application/json: - schema: {} - "404": - description: capture does not exist for this flow - - /w/{workspace}/favorites/star: - post: - summary: star item - operationId: star - tags: - - favorite - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - content: - application/json: - schema: - type: object - properties: - path: - type: string - favorite_kind: - type: string - enum: [flow, app, script, raw_app] - responses: - "200": - description: star item - - /w/{workspace}/favorites/unstar: - post: - summary: unstar item - operationId: unstar - tags: - - favorite - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - content: - application/json: - schema: - type: object - properties: - path: - type: string - favorite_kind: - type: string - enum: [flow, app, script, raw_app] - responses: - "200": - description: unstar item - - /w/{workspace}/inputs/history: - get: - summary: List Inputs used in previously completed jobs - operationId: getInputHistory - tags: - - input - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/RunnableId" - - $ref: "#/components/parameters/RunnableTypeQuery" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: Input history for completed jobs - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Input" - - /w/{workspace}/inputs/{jobOrInputId}/args: - get: - summary: Get args from history or saved input - operationId: getArgsFromHistoryOrSavedInput - tags: - - input - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: jobOrInputId - in: path - required: true - schema: - type: string - - name: input - in: query - schema: - type: boolean - - name: allow_large - in: query - schema: - type: boolean - responses: - "200": - description: args - content: - application/json: - schema: {} - - /w/{workspace}/inputs/list: - get: - summary: List saved Inputs for a Runnable - operationId: listInputs - tags: - - input - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/RunnableId" - - $ref: "#/components/parameters/RunnableTypeQuery" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - responses: - "200": - description: Saved Inputs for a Runnable - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Input" - - /w/{workspace}/inputs/create: - post: - summary: Create an Input for future use in a script or flow - operationId: createInput - tags: - - input - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/RunnableId" - - $ref: "#/components/parameters/RunnableTypeQuery" - requestBody: - description: Input - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateInput" - responses: - "201": - description: Input created - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/inputs/update: - post: - summary: Update an Input - operationId: updateInput - tags: - - input - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: UpdateInput - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateInput" - responses: - "201": - description: Input updated - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/inputs/delete/{input}: - post: - summary: Delete a Saved Input - operationId: deleteInput - tags: - - input - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/InputId" - responses: - "200": - description: Input deleted - content: - text/plain: - schema: - type: string - format: uuid - - /w/{workspace}/job_helpers/duckdb_connection_settings: - post: - summary: - Converts an S3 resource to the set of instructions necessary to connect - DuckDB to an S3 bucket - operationId: duckdbConnectionSettings - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: S3 resource to connect to - required: true - content: - application/json: - schema: - type: object - properties: - s3_resource: - $ref: "#/components/schemas/S3Resource" - responses: - "200": - description: Connection settings - content: - application/json: - schema: - type: object - properties: - connection_settings_str: - type: string - /w/{workspace}/job_helpers/v2/duckdb_connection_settings: - post: - summary: - Converts an S3 resource to the set of instructions necessary to connect - DuckDB to an S3 bucket - operationId: duckdbConnectionSettingsV2 - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: - S3 resource path to use to generate the connection settings. If empty, - the S3 resource defined in the workspace settings will be used - required: true - content: - application/json: - schema: - type: object - properties: - s3_resource_path: - type: string - responses: - "200": - description: Connection settings - content: - application/json: - schema: - type: object - properties: - connection_settings_str: - type: string - required: - - connection_settings_str - - /w/{workspace}/job_helpers/polars_connection_settings: - post: - summary: - Converts an S3 resource to the set of arguments necessary to connect - Polars to an S3 bucket - operationId: polarsConnectionSettings - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: S3 resource to connect to - required: true - content: - application/json: - schema: - type: object - properties: - s3_resource: - $ref: "#/components/schemas/S3Resource" - responses: - "200": - description: Connection settings - content: - application/json: - schema: - type: object - properties: - endpoint_url: - type: string - key: - type: string - secret: - type: string - use_ssl: - type: boolean - cache_regions: - type: boolean - client_kwargs: - $ref: "#/components/schemas/PolarsClientKwargs" - required: - - endpoint_url - - use_ssl - - cache_regions - - client_kwargs - /w/{workspace}/job_helpers/v2/polars_connection_settings: - post: - summary: - Converts an S3 resource to the set of arguments necessary to connect - Polars to an S3 bucket - operationId: polarsConnectionSettingsV2 - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: - S3 resource path to use to generate the connection settings. If empty, - the S3 resource defined in the workspace settings will be used - required: true - content: - application/json: - schema: - type: object - properties: - s3_resource_path: - type: string - responses: - "200": - description: Connection settings - content: - application/json: - schema: - type: object - properties: - s3fs_args: - type: object - properties: - endpoint_url: - type: string - key: - type: string - secret: - type: string - use_ssl: - type: boolean - cache_regions: - type: boolean - client_kwargs: - $ref: "#/components/schemas/PolarsClientKwargs" - required: - - endpoint_url - - use_ssl - - cache_regions - - client_kwargs - storage_options: - type: object - properties: - aws_endpoint_url: - type: string - aws_access_key_id: - type: string - aws_secret_access_key: - type: string - aws_region: - type: string - aws_allow_http: - type: string - required: - - aws_endpoint_url - - aws_region - - aws_allow_http - required: - - s3fs_args - - storage_options - /w/{workspace}/job_helpers/v2/s3_resource_info: - post: - summary: Returns the s3 resource associated to the provided path, or the - workspace default S3 resource - operationId: s3ResourceInfo - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: - S3 resource path to use. If empty, the S3 resource defined in the - workspace settings will be used - required: true - content: - application/json: - schema: - type: object - properties: - s3_resource_path: - type: string - responses: - "200": - description: Connection settings - content: - application/json: - schema: - $ref: "#/components/schemas/S3Resource" - - /w/{workspace}/job_helpers/test_connection: - get: - summary: Test connection to the workspace object storage - operationId: datasetStorageTestConnection - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: storage - in: query - schema: - type: string - responses: - "200": - description: Connection settings - content: - application/json: - schema: {} - - /w/{workspace}/job_helpers/list_stored_files: - get: - summary: List the file keys available in a workspace object storage - operationId: listStoredFiles - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: max_keys - in: query - required: true - schema: - type: integer - - name: marker - in: query - schema: - type: string - - name: prefix - in: query - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: List of file keys - content: - application/json: - schema: - type: object - properties: - next_marker: - type: string - windmill_large_files: - type: array - items: - $ref: "#/components/schemas/WindmillLargeFile" - restricted_access: - type: boolean - required: - - windmill_large_files - - /w/{workspace}/job_helpers/load_file_metadata: - get: - summary: Load metadata of the file - operationId: loadFileMetadata - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: file_key - in: query - required: true - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: FileMetadata - content: - application/json: - schema: - $ref: "#/components/schemas/WindmillFileMetadata" - - /w/{workspace}/job_helpers/load_file_preview: - get: - summary: Load a preview of the file - operationId: loadFilePreview - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: file_key - in: query - required: true - schema: - type: string - - name: file_size_in_bytes - in: query - schema: - type: integer - - name: file_mime_type - in: query - schema: - type: string - - name: csv_separator - in: query - schema: - type: string - - name: csv_has_header - in: query - schema: - type: boolean - - name: read_bytes_from - in: query - schema: - type: integer - - name: read_bytes_length - in: query - schema: - type: integer - - name: storage - in: query - schema: - type: string - responses: - "200": - description: FilePreview - content: - application/json: - schema: - $ref: "#/components/schemas/WindmillFilePreview" - - /w/{workspace}/job_helpers/load_parquet_preview/{path}: - get: - summary: Load a preview of a parquet file - operationId: loadParquetPreview - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: offset - in: query - schema: - type: number - - name: limit - in: query - schema: - type: number - - name: sort_col - in: query - schema: - type: string - - name: sort_desc - in: query - schema: - type: boolean - - name: search_col - in: query - schema: - type: string - - name: search_term - in: query - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: Parquet Preview - content: - application/json: - schema: {} - - /w/{workspace}/job_helpers/load_table_count/{path}: - get: - summary: Load the table row count - operationId: loadTableRowCount - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: search_col - in: query - schema: - type: string - - name: search_term - in: query - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: Table count - content: - application/json: - schema: - type: object - properties: - count: - type: number - - /w/{workspace}/job_helpers/load_csv_preview/{path}: - get: - summary: Load a preview of a csv file - operationId: loadCsvPreview - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - - name: offset - in: query - schema: - type: number - - name: limit - in: query - schema: - type: number - - name: sort_col - in: query - schema: - type: string - - name: sort_desc - in: query - schema: - type: boolean - - name: search_col - in: query - schema: - type: string - - name: search_term - in: query - schema: - type: string - - name: storage - in: query - schema: - type: string - - name: csv_separator - in: query - schema: - type: string - responses: - "200": - description: Csv Preview - content: - application/json: - schema: {} - - /w/{workspace}/job_helpers/delete_s3_file: - delete: - summary: Permanently delete file from S3 - operationId: deleteS3File - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: file_key - in: query - required: true - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: Confirmation - content: - application/json: - schema: {} - - /w/{workspace}/job_helpers/move_s3_file: - get: - summary: Move a S3 file from one path to the other within the same bucket - operationId: moveS3File - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: src_file_key - in: query - required: true - schema: - type: string - - name: dest_file_key - in: query - required: true - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: Confirmation - content: - application/json: - schema: {} - - /w/{workspace}/job_helpers/upload_s3_file: - post: - summary: Upload file to S3 bucket - operationId: fileUpload - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: file_key - in: query - required: false - schema: - type: string - - name: file_extension - in: query - required: false - schema: - type: string - - name: s3_resource_path - in: query - required: false - schema: - type: string - - name: resource_type - in: query - required: false - schema: - type: string - - name: storage - in: query - schema: - type: string - - name: content_type - in: query - schema: - type: string - - name: content_disposition - in: query - schema: - type: string - requestBody: - description: File content - required: true - content: - application/octet-stream: - schema: - type: string - format: binary - responses: - "200": - description: File upload status - content: - application/json: - schema: - type: object - properties: - file_key: - type: string - required: - - file_key - - /w/{workspace}/job_helpers/download_s3_file: - get: - summary: Download file to S3 bucket - operationId: fileDownload - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: file_key - in: query - required: true - schema: - type: string - - name: s3_resource_path - in: query - required: false - schema: - type: string - - name: resource_type - in: query - required: false - schema: - type: string - - name: storage - in: query - schema: - type: string - responses: - "200": - description: Chunk of the downloaded file - content: - application/octet-stream: - schema: - type: string - format: binary - - /w/{workspace}/job_helpers/download_s3_parquet_file_as_csv: - get: - summary: Download file to S3 bucket - operationId: fileDownloadParquetAsCsv - tags: - - helpers - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: file_key - in: query - required: true - schema: - type: string - - name: s3_resource_path - in: query - required: false - schema: - type: string - - name: resource_type - in: query - required: false - schema: - type: string - responses: - "200": - description: The downloaded file - content: - text/csv: - schema: - type: string - - /w/{workspace}/job_metrics/get/{id}: - post: - summary: get job metrics - operationId: getJobMetrics - tags: - - metrics - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - requestBody: - description: parameters for statistics retrieval - required: true - content: - application/json: - schema: - type: object - properties: - timeseries_max_datapoints: - type: integer - from_timestamp: - type: string - format: date-time - to_timestamp: - type: string - format: date-time - - responses: - "200": - description: job details - content: - application/json: - schema: - type: object - properties: - metrics_metadata: - type: array - items: - $ref: "#/components/schemas/MetricMetadata" - scalar_metrics: - type: array - items: - $ref: "#/components/schemas/ScalarMetric" - timeseries_metrics: - type: array - items: - $ref: "#/components/schemas/TimeseriesMetric" - - /w/{workspace}/job_metrics/set_progress/{id}: - post: - summary: set job metrics - operationId: setJobProgress - tags: - - metrics - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - requestBody: - description: parameters for statistics retrieval - required: true - content: - application/json: - schema: - type: object - properties: - percent: - type: integer - flow_job_id: - type: string - format: uuid - - responses: - "200": - description: Job progress updated - content: - application/json: - schema: {} - - /w/{workspace}/job_metrics/get_progress/{id}: - get: - summary: get job progress - operationId: getJobProgress - tags: - - metrics - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: job progress between 0 and 99 - content: - application/json: - schema: - type: integer - /service_logs/list_files: - get: - summary: list log files ordered by timestamp - operationId: listLogFiles - tags: - - service_logs - parameters: - - $ref: "#/components/parameters/Before" - - $ref: "#/components/parameters/After" - - name: with_error - in: query - required: false - schema: - type: boolean - responses: - "200": - description: time - content: - application/json: - schema: - type: array - items: - type: object - properties: - hostname: - type: string - mode: - type: string - worker_group: - type: string - log_ts: - type: string - format: date-time - file_path: - type: string - ok_lines: - type: integer - err_lines: - type: integer - json_fmt: - type: boolean - required: - - hostname - - mode - - log_ts - - file_path - - json_fmt - - /service_logs/get_log_file/{path}: - get: - summary: get log file by path - operationId: getLogFile - tags: - - service_logs - parameters: - - $ref: "#/components/parameters/Path" - responses: - "200": - description: log stream - content: - text/plain: - schema: - type: string - - /concurrency_groups/list: - get: - summary: List all concurrency groups - operationId: listConcurrencyGroups - tags: - - concurrencyGroups - responses: - "200": - description: all concurrency groups - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ConcurrencyGroup" - /concurrency_groups/prune/{concurrency_id}: - delete: - summary: Delete concurrency group - operationId: deleteConcurrencyGroup - tags: - - concurrencyGroups - parameters: - - $ref: "#/components/parameters/ConcurrencyId" - responses: - "200": - description: concurrency group removed - content: - application/json: - schema: - type: object - properties: {} - /concurrency_groups/{id}/key: - get: - summary: Get the concurrency key for a job that has concurrency limits enabled - operationId: getConcurrencyKey - tags: - - concurrencyGroups - parameters: - - $ref: "#/components/parameters/JobId" - responses: - "200": - description: concurrency key for given job - content: - application/json: - schema: - type: string - /w/{workspace}/concurrency_groups/list_jobs: - get: - summary: Get intervals of job runtime concurrency - operationId: listExtendedJobs - tags: - - concurrencyGroups - - job - parameters: - - name: concurrency_key - in: query - required: false - schema: - type: string - - name: row_limit - in: query - required: false - schema: - type: number - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/CreatedBy" - - $ref: "#/components/parameters/Label" - - $ref: "#/components/parameters/ParentJob" - - $ref: "#/components/parameters/ScriptExactPath" - - $ref: "#/components/parameters/ScriptStartPath" - - $ref: "#/components/parameters/SchedulePath" - - $ref: "#/components/parameters/ScriptExactHash" - - $ref: "#/components/parameters/StartedBefore" - - $ref: "#/components/parameters/StartedAfter" - - $ref: "#/components/parameters/CreatedOrStartedBefore" - - $ref: "#/components/parameters/Running" - - $ref: "#/components/parameters/ScheduledForBeforeNow" - - $ref: "#/components/parameters/CreatedOrStartedAfter" - - $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob" - - $ref: "#/components/parameters/JobKinds" - - $ref: "#/components/parameters/ArgsFilter" - - $ref: "#/components/parameters/Tag" - - $ref: "#/components/parameters/ResultFilter" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PerPage" - - name: is_skipped - description: is the job skipped - in: query - schema: - type: boolean - - name: is_flow_step - description: is the job a flow step - in: query - schema: - type: boolean - - name: has_null_parent - description: has null parent - in: query - schema: - type: boolean - - name: success - description: filter on successful jobs - in: query - schema: - type: boolean - - name: all_workspaces - description: get jobs from all workspaces (only valid if request come from the `admins` workspace) - in: query - schema: - type: boolean - - name: is_not_schedule - description: is not a scheduled job - in: query - schema: - type: boolean - responses: - "200": - description: time - content: - application/json: - schema: - $ref: "#/components/schemas/ExtendedJobs" - - /srch/w/{workspace}/index/search/job: - get: - summary: Search through jobs with a string query - operationId: searchJobsIndex - tags: - - indexSearch - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - name: search_query - in: query - required: true - schema: - type: string - responses: - "200": - description: search results - content: - application/json: - schema: - type: object - properties: - query_parse_errors: - description: a list of the terms that couldn't be parsed (and thus ignored) - type: array - items: - type: object - properties: - dancer: - type: string - hits: - description: the jobs that matched the query - type: array - items: - $ref: "#/components/schemas/JobSearchHit" - - /srch/index/search/service_logs: - get: - summary: Search through service logs with a string query - operationId: searchLogsIndex - tags: - - indexSearch - parameters: - - name: search_query - in: query - required: true - schema: - type: string - - name: mode - in: query - required: true - schema: - type: string - - name: worker_group - in: query - required: false - schema: - type: string - - name: hostname - in: query - required: true - schema: - type: string - - name: min_ts - in: query - required: false - schema: - type: string - format: date-time - - name: max_ts - in: query - required: false - schema: - type: string - format: date-time - responses: - "200": - description: search results - content: - application/json: - schema: - type: object - properties: - query_parse_errors: - description: a list of the terms that couldn't be parsed (and thus ignored) - type: array - items: - type: string - hits: - description: log files that matched the query - type: array - items: - $ref: "#/components/schemas/LogSearchHit" - - /srch/index/search/count_service_logs: - get: - summary: Search and count the log line hits on every provided host - operationId: countSearchLogsIndex - tags: - - indexSearch - parameters: - - name: search_query - in: query - required: true - schema: - type: string - - name: hosts - in: query - required: true - schema: - type: string - - name: min_ts - in: query - required: false - schema: - type: string - format: date-time - - name: max_ts - in: query - required: false - schema: - type: string - format: date-time - responses: - "200": - description: search results - content: - application/json: - schema: - type: object - properties: - query_parse_errors: - description: a list of the terms that couldn't be parsed (and thus ignored) - type: array - items: - type: string - count_per_host: - description: count of log lines that matched the query per hostname - type: object - -components: - securitySchemes: - bearerAuth: - type: http - scheme: bearer - cookieAuth: - type: apiKey - in: cookie - name: token - - parameters: - Key: - name: key - in: path - required: true - schema: - type: string - WorkspaceId: - name: workspace - in: path - required: true - schema: - type: string - VersionId: - name: version - in: path - required: true - schema: - type: number - Token: - name: token - in: path - required: true - schema: - type: string - AccountId: - name: id - in: path - required: true - schema: - type: integer - ClientName: - name: client_name - in: path - required: true - schema: - type: string - ScriptPath: - name: path - in: path - required: true - schema: - type: string - ScriptHash: - name: hash - in: path - required: true - schema: - type: string - JobId: - name: id - in: path - required: true - schema: - type: string - format: uuid - Path: - name: path - in: path - required: true - schema: - type: string - PathId: - name: id - in: path - required: true - schema: - type: integer - PathVersion: - name: version - in: path - required: true - schema: - type: integer - Name: - name: name - in: path - required: true - schema: - type: string - Page: - name: page - description: which page to return (start at 1, default 1) - in: query - schema: - type: integer - PerPage: - name: per_page - description: number of items to return for a given page (default 30, max 100) - in: query - schema: - type: integer - OrderDesc: - name: order_desc - description: order by desc order (default true) - in: query - schema: - type: boolean - CreatedBy: - name: created_by - description: mask to filter exact matching user creator - in: query - schema: - type: string - Label: - name: label - description: mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') - in: query - schema: - type: string - ParentJob: - name: parent_job - description: - The parent job that is at the origin and responsible for the execution - of this script if any - in: query - schema: - type: string - format: uuid - WorkerTag: - name: tag - description: Override the tag to use - in: query - schema: - type: string - CacheTtl: - name: cache_ttl - description: Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl - in: query - schema: - type: string - NewJobId: - name: job_id - description: - The job id to assign to the created job. if missing, job is chosen - randomly using the ULID scheme. If a job id already exists in the queue - or as a completed job, the request to create one will fail (Bad Request) - in: query - schema: - type: string - format: uuid - IncludeHeader: - name: include_header - description: | - List of headers's keys (separated with ',') whove value are added to the args - Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - in: query - schema: - type: string - QueueLimit: - name: queue_limit - description: | - The maximum size of the queue for which the request would get rejected if that job would push it above that limit - in: query - schema: - type: string - Payload: - name: payload - description: | - The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent - `encodeURIComponent(btoa(JSON.stringify({a: 2})))` - in: query - schema: - type: string - ScriptStartPath: - name: script_path_start - description: mask to filter matching starting path - in: query - schema: - type: string - SchedulePath: - name: schedule_path - description: mask to filter by schedule path - in: query - schema: - type: string - ScriptExactPath: - name: script_path_exact - description: mask to filter exact matching path - in: query - schema: - type: string - ScriptExactHash: - name: script_hash - description: mask to filter exact matching path - in: query - schema: - type: string - CreatedBefore: - name: created_before - description: filter on created before (inclusive) timestamp - in: query - schema: - type: string - format: date-time - CreatedAfter: - name: created_after - description: filter on created after (exclusive) timestamp - in: query - schema: - type: string - format: date-time - StartedBefore: - name: started_before - description: filter on started before (inclusive) timestamp - in: query - schema: - type: string - format: date-time - StartedAfter: - name: started_after - description: filter on started after (exclusive) timestamp - in: query - schema: - type: string - format: date-time - Before: - name: before - description: filter on started before (inclusive) timestamp - in: query - schema: - type: string - format: date-time - CreatedOrStartedAfter: - name: created_or_started_after - description: - filter on created_at for non non started job and started_at otherwise - after (exclusive) timestamp - in: query - schema: - type: string - format: date-time - CreatedOrStartedAfterCompletedJob: - name: created_or_started_after_completed_jobs - description: - filter on created_at for non non started job and started_at otherwise - after (exclusive) timestamp but only for the completed jobs - in: query - schema: - type: string - format: date-time - CreatedOrStartedBefore: - name: created_or_started_before - description: - filter on created_at for non non started job and started_at otherwise - before (inclusive) timestamp - in: query - schema: - type: string - format: date-time - Success: - name: success - description: filter on successful jobs - in: query - schema: - type: boolean - ScheduledForBeforeNow: - name: scheduled_for_before_now - description: filter on jobs scheduled_for before now (hence waitinf for a worker) - in: query - schema: - type: boolean - Suspended: - name: suspended - description: filter on suspended jobs - in: query - schema: - type: boolean - Running: - name: running - description: filter on running jobs - in: query - schema: - type: boolean - ArgsFilter: - name: args - description: filter on jobs containing those args as a json subset (@> in postgres) - in: query - schema: - type: string - Tag: - name: tag - description: filter on jobs with a given tag/worker group - in: query - schema: - type: string - ResultFilter: - name: result - description: filter on jobs containing those result as a json subset (@> in postgres) - in: query - schema: - type: string - After: - name: after - description: filter on created after (exclusive) timestamp - in: query - schema: - type: string - format: date-time - Username: - name: username - description: filter on exact username of user - in: query - schema: - type: string - Operation: - name: operation - description: filter on exact or prefix name of operation - in: query - schema: - type: string - ResourceName: - name: resource - description: filter on exact or prefix name of resource - in: query - schema: - type: string - ActionKind: - name: action_kind - description: filter on type of operation - in: query - schema: - type: string - enum: [Create, Update, Delete, Execute] - JobKinds: - name: job_kinds - description: - filter on job kind (values 'preview', 'script', 'dependencies', 'flow') - separated by, - in: query - schema: - type: string - # correct type is below but explode not supported by our codegen - # type: array - # items: - # type: string - # enum: ["preview", "script", "dependencies"] - # explode: false - RunnableId: - name: runnable_id - in: query - schema: - type: string - RunnableTypeQuery: - name: runnable_type - in: query - schema: - $ref: "#/components/schemas/RunnableType" - InputId: - name: input - in: path - required: true - schema: - type: string - GetStarted: - name: get_started - in: query - schema: - type: boolean - ConcurrencyId: - name: concurrency_id - in: path - required: true - schema: - type: string - - schemas: - $ref: "../../openflow.openapi.yaml#/components/schemas" - - AiResource: - type: object - properties: - path: - type: string - provider: - type: string - required: - - path - - provider - Script: - type: object - properties: - workspace_id: - type: string - hash: - type: string - path: - type: string - parent_hashes: - type: array - description: | - The first element is the direct parent of the script, the second is the parent of the first, etc - items: - type: string - summary: - type: string - description: - type: string - content: - type: string - created_by: - type: string - created_at: - type: string - format: date-time - archived: - type: boolean - schema: - type: object - deleted: - type: boolean - is_template: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - lock: - type: string - lock_error_logs: - type: string - language: - type: string - enum: - [ - python3, - deno, - go, - bash, - powershell, - postgresql, - mysql, - bigquery, - snowflake, - mssql, - graphql, - nativets, - bun, - php, - rust, - ansible, - ] - kind: - type: string - enum: [script, failure, trigger, command, approval] - starred: - type: boolean - tag: - type: string - has_draft: - type: boolean - draft_only: - type: boolean - envs: - type: array - items: - type: string - concurrent_limit: - type: integer - concurrency_time_window_s: - type: integer - concurrency_key: - type: string - cache_ttl: - type: number - dedicated_worker: - type: boolean - ws_error_handler_muted: - type: boolean - priority: - type: integer - restart_unless_cancelled: - type: boolean - timeout: - type: integer - delete_after_use: - type: boolean - visible_to_runner_only: - type: boolean - no_main_func: - type: boolean - codebase: - type: string - has_preprocessor: - type: boolean - - required: - - hash - - path - - summary - - description - - content - - created_by - - created_at - - archived - - deleted - - is_template - - extra_perms - - language - - kind - - starred - - no_main_func - - has_preprocessor - - - NewScript: - type: object - properties: - path: - type: string - parent_hash: - type: string - summary: - type: string - description: - type: string - content: - type: string - schema: - type: object - is_template: - type: boolean - lock: - type: string - language: - type: string - enum: - [ - python3, - deno, - go, - bash, - powershell, - postgresql, - mysql, - bigquery, - snowflake, - mssql, - graphql, - nativets, - bun, - php, - rust, - ansible, - ] - kind: - type: string - enum: [script, failure, trigger, command, approval] - tag: - type: string - draft_only: - type: boolean - envs: - type: array - items: - type: string - concurrent_limit: - type: integer - concurrency_time_window_s: - type: integer - cache_ttl: - type: number - dedicated_worker: - type: boolean - ws_error_handler_muted: - type: boolean - priority: - type: integer - restart_unless_cancelled: - type: boolean - timeout: - type: integer - delete_after_use: - type: boolean - deployment_message: - type: string - concurrency_key: - type: string - visible_to_runner_only: - type: boolean - no_main_func: - type: boolean - codebase: - type: string - has_preprocessor: - type: boolean - required: - - path - - summary - - description - - content - - language - - NewScriptWithDraft: - allOf: - - $ref: "#/components/schemas/NewScript" - - type: object - properties: - draft: - $ref: "#/components/schemas/NewScript" - hash: - type: string - required: - - hash - - ScriptHistory: - type: object - properties: - script_hash: - type: string - deployment_msg: - type: string - required: - - script_hash - - ScriptArgs: - type: object - additionalProperties: {} - - Input: - type: object - properties: - id: - type: string - name: - type: string - created_by: - type: string - created_at: - type: string - format: date-time - is_public: - type: boolean - success: - type: boolean - required: - - id - - name - - args - - created_by - - created_at - - is_public - - CreateInput: - type: object - properties: - name: - type: string - args: - type: object - required: - - name - - args - - created_by - - UpdateInput: - type: object - properties: - id: - type: string - name: - type: string - is_public: - type: boolean - required: - - id - - name - - is_public - - RunnableType: - type: string - enum: ["ScriptHash", "ScriptPath", "FlowPath"] - - QueuedJob: - type: object - properties: - workspace_id: - type: string - id: - type: string - format: uuid - parent_job: - type: string - format: uuid - created_by: - type: string - created_at: - type: string - format: date-time - started_at: - type: string - format: date-time - scheduled_for: - type: string - format: date-time - running: - type: boolean - script_path: - type: string - script_hash: - type: string - args: - $ref: "#/components/schemas/ScriptArgs" - logs: - type: string - raw_code: - type: string - canceled: - type: boolean - canceled_by: - type: string - canceled_reason: - type: string - last_ping: - type: string - format: date-time - job_kind: - type: string - enum: - [ - "script", - "preview", - "dependencies", - "flowdependencies", - "appdependencies", - "flow", - "flowpreview", - "script_hub", - "identity", - "deploymentcallback", - "singlescriptflow", - ] - schedule_path: - type: string - permissioned_as: - type: string - description: | - The user (u/userfoo) or group (g/groupfoo) whom - the execution of this script will be permissioned_as and by extension its DT_TOKEN. - flow_status: - $ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus" - raw_flow: - $ref: "../../openflow.openapi.yaml#/components/schemas/FlowValue" - is_flow_step: - type: boolean - language: - type: string - enum: - [ - python3, - deno, - go, - bash, - powershell, - postgresql, - mysql, - bigquery, - snowflake, - mssql, - graphql, - nativets, - bun, - php, - rust, - ansible, - ] - email: - type: string - visible_to_owner: - type: boolean - mem_peak: - type: integer - tag: - type: string - priority: - type: integer - self_wait_time_ms: - type: number - aggregate_wait_time_ms: - type: number - suspend: - type: number - required: - - id - - running - - canceled - - job_kind - - permissioned_as - - is_flow_step - - email - - visible_to_owner - - tag - - CompletedJob: - type: object - properties: - workspace_id: - type: string - id: - type: string - format: uuid - parent_job: - type: string - format: uuid - created_by: - type: string - created_at: - type: string - format: date-time - started_at: - type: string - format: date-time - duration_ms: - type: integer - success: - type: boolean - script_path: - type: string - script_hash: - type: string - args: - $ref: "#/components/schemas/ScriptArgs" - result: {} - logs: - type: string - deleted: - type: boolean - raw_code: - type: string - canceled: - type: boolean - canceled_by: - type: string - canceled_reason: - type: string - job_kind: - type: string - enum: - [ - "script", - "preview", - "dependencies", - "flow", - "flowdependencies", - "appdependencies", - "flowpreview", - "script_hub", - "identity", - "deploymentcallback", - "singlescriptflow", - ] - schedule_path: - type: string - permissioned_as: - type: string - description: | - The user (u/userfoo) or group (g/groupfoo) whom - the execution of this script will be permissioned_as and by extension its DT_TOKEN. - flow_status: - $ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus" - raw_flow: - $ref: "../../openflow.openapi.yaml#/components/schemas/FlowValue" - is_flow_step: - type: boolean - language: - type: string - enum: - [ - python3, - deno, - go, - bash, - powershell, - postgresql, - mysql, - bigquery, - snowflake, - mssql, - graphql, - nativets, - bun, - php, - rust, - ansible, - ] - is_skipped: - type: boolean - email: - type: string - visible_to_owner: - type: boolean - mem_peak: - type: integer - tag: - type: string - priority: - type: integer - labels: - type: array - items: - type: string - self_wait_time_ms: - type: number - aggregate_wait_time_ms: - type: number - required: - - id - - created_by - - duration_ms - - created_at - - started_at - - success - - canceled - - job_kind - - permissioned_as - - is_flow_step - - is_skipped - - email - - visible_to_owner - - tag - - ObscuredJob: - type: object - properties: - typ: - type: string - started_at: - type: string - format: date-time - duration_ms: - type: number - Job: - oneOf: - - allOf: - - $ref: "#/components/schemas/CompletedJob" - - type: object - properties: - type: - type: string - enum: [CompletedJob] - - allOf: - - $ref: "#/components/schemas/QueuedJob" - - type: object - properties: - type: - type: string - enum: [QueuedJob] - discriminator: - propertyName: type - - User: - type: object - properties: - email: - type: string - username: - type: string - is_admin: - type: boolean - is_super_admin: - type: boolean - created_at: - type: string - format: date-time - operator: - type: boolean - disabled: - type: boolean - groups: - type: array - items: - type: string - folders: - type: array - items: - type: string - folders_owners: - type: array - items: - type: string - required: - - email - - username - - is_admin - - is_super_admin - - created_at - - operator - - disabled - - folders - - folders_owners - - UserUsage: - type: object - properties: - email: - type: string - executions: - type: number - - Login: - type: object - properties: - email: - type: string - password: - type: string - required: - - email - - password - - EditWorkspaceUser: - type: object - properties: - is_admin: - type: boolean - operator: - type: boolean - disabled: - type: boolean - - TruncatedToken: - type: object - properties: - label: - type: string - expiration: - type: string - format: date-time - token_prefix: - type: string - created_at: - type: string - format: date-time - last_used_at: - type: string - format: date-time - scopes: - type: array - items: - type: string - email: - type: string - required: - - token_prefix - - created_at - - last_used_at - - NewToken: - type: object - properties: - label: - type: string - expiration: - type: string - format: date-time - scopes: - type: array - items: - type: string - workspace_id: - type: string - - NewTokenImpersonate: - type: object - properties: - label: - type: string - expiration: - type: string - format: date-time - impersonate_email: - type: string - workspace_id: - type: string - required: - - impersonate_email - - ListableVariable: - type: object - properties: - workspace_id: - type: string - path: - type: string - value: - type: string - is_secret: - type: boolean - description: - type: string - account: - type: integer - is_oauth: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - is_expired: - type: boolean - refresh_error: - type: string - is_linked: - type: boolean - is_refreshed: - type: boolean - expires_at: - type: string - format: date-time - required: - - workspace_id - - path - - is_secret - - extra_perms - - ContextualVariable: - type: object - properties: - name: - type: string - value: - type: string - description: - type: string - is_custom: - type: boolean - required: - - name - - value - - description - - is_custom - - CreateVariable: - type: object - properties: - path: - type: string - value: - type: string - is_secret: - type: boolean - description: - type: string - account: - type: integer - is_oauth: - type: boolean - expires_at: - type: string - format: date-time - required: - - path - - value - - is_secret - - description - - EditVariable: - type: object - properties: - path: - type: string - value: - type: string - is_secret: - type: boolean - description: - type: string - - AuditLog: - type: object - properties: - id: - type: integer - timestamp: - type: string - format: date-time - username: - type: string - operation: - type: string - enum: - - "jobs.run" - - "jobs.run.script" - - "jobs.run.preview" - - "jobs.run.flow" - - "jobs.run.flow_preview" - - "jobs.run.script_hub" - - "jobs.run.dependencies" - - "jobs.run.identity" - - "jobs.run.noop" - - "jobs.flow_dependencies" - - "jobs" - - "jobs.cancel" - - "jobs.force_cancel" - - "jobs.disapproval" - - "jobs.delete" - - "account.delete" - - "ai.request" - - "resources.create" - - "resources.update" - - "resources.delete" - - "resource_types.create" - - "resource_types.update" - - "resource_types.delete" - - "schedule.create" - - "schedule.setenabled" - - "schedule.edit" - - "schedule.delete" - - "scripts.create" - - "scripts.update" - - "scripts.archive" - - "scripts.delete" - - "users.create" - - "users.delete" - - "users.update" - - "users.login" - - "users.login_failure" - - "users.logout" - - "users.accept_invite" - - "users.decline_invite" - - "users.token.create" - - "users.token.delete" - - "users.add_to_workspace" - - "users.add_global" - - "users.setpassword" - - "users.impersonate" - - "users.leave_workspace" - - "oauth.login" - - "oauth.login_failure" - - "oauth.signup" - - "variables.create" - - "variables.delete" - - "variables.update" - - "flows.create" - - "flows.update" - - "flows.delete" - - "flows.archive" - - "apps.create" - - "apps.update" - - "apps.delete" - - "folder.create" - - "folder.update" - - "folder.delete" - - "folder.add_owner" - - "folder.remove_owner" - - "group.create" - - "group.delete" - - "group.edit" - - "group.adduser" - - "group.removeuser" - - "igroup.create" - - "igroup.delete" - - "igroup.adduser" - - "igroup.removeuser" - - "variables.decrypt_secret" - - "workspaces.edit_command_script" - - "workspaces.edit_deploy_to" - - "workspaces.edit_auto_invite_domain" - - "workspaces.edit_webhook" - - "workspaces.edit_copilot_config" - - "workspaces.edit_error_handler" - - "workspaces.create" - - "workspaces.update" - - "workspaces.archive" - - "workspaces.unarchive" - - "workspaces.delete" - action_kind: - type: string - enum: ["Created", "Updated", "Delete", "Execute"] - resource: - type: string - parameters: - type: object - required: - - id - - timestamp - - username - - operation - - action_kind - - MainArgSignature: - type: object - properties: - type: - type: string - enum: ["Valid", "Invalid"] - error: - type: string - star_args: - type: boolean - star_kwargs: - type: boolean - args: - type: array - items: - type: object - properties: - name: - type: string - typ: - oneOf: - - type: string - enum: - [ - "float", - "int", - "bool", - "email", - "unknown", - "bytes", - "dict", - "datetime", - "sql", - ] - - type: object - properties: - resource: - type: string - nullable: true - required: - - resource - - type: object - properties: - str: - type: array - items: - type: string - nullable: true - required: - - str - - type: object - properties: - object: - type: array - items: - type: object - properties: - key: - type: string - typ: - oneOf: - - type: string - enum: - [ - "float", - "int", - "bool", - "email", - "unknown", - "bytes", - "dict", - "datetime", - "sql", - ] - - type: object - properties: - str: {} - required: [str] - required: - - key - - typ - required: - - object - - type: object - properties: - list: - oneOf: - - type: string - enum: - [ - "float", - "int", - "bool", - "email", - "unknown", - "bytes", - "dict", - "datetime", - "sql", - ] - - type: object - properties: - str: {} - required: [str] - nullable: true - required: - - list - has_default: - type: boolean - default: {} - required: - - name - - typ - no_main_func: - type: boolean - nullable: true - has_preprocessor: - type: boolean - nullable: true - required: - - star_args - - start_kwargs - - args - - type - - error - - no_main_func - - has_preprocessor - - Preview: - type: object - properties: - content: - type: string - path: - type: string - args: - $ref: "#/components/schemas/ScriptArgs" - language: - type: string - enum: - [ - python3, - deno, - go, - bash, - powershell, - postgresql, - mysql, - bigquery, - snowflake, - mssql, - graphql, - nativets, - bun, - php, - rust, - ansible, - ] - tag: - type: string - kind: - type: string - enum: [code, identity, http] - dedicated_worker: - type: boolean - lock: - type: string - required: - - args - - WorkflowTask: - type: object - properties: - args: - $ref: "#/components/schemas/ScriptArgs" - required: - - args - - WorkflowStatusRecord: - type: object - additionalProperties: - $ref: "#/components/schemas/WorkflowStatus" - - WorkflowStatus: - type: object - properties: - scheduled_for: - type: string - format: date-time - started_at: - type: string - format: date-time - duration_ms: - type: number - name: - type: string - - CreateResource: - type: object - properties: - path: - type: string - value: {} - description: - type: string - resource_type: - type: string - required: - - path - - value - - resource_type - - EditResource: - type: object - properties: - path: - type: string - description: - type: string - value: {} - - Resource: - type: object - properties: - workspace_id: - type: string - path: - type: string - description: - type: string - resource_type: - type: string - value: {} - is_oauth: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - created_by: - type: string - edited_at: - type: string - format: date-time - required: - - path - - resource_type - - is_oauth - - ListableResource: - type: object - properties: - workspace_id: - type: string - path: - type: string - description: - type: string - resource_type: - type: string - value: {} - is_oauth: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - is_expired: - type: boolean - refresh_error: - type: string - is_linked: - type: boolean - is_refreshed: - type: boolean - account: - type: number - created_by: - type: string - edited_at: - type: string - format: date-time - required: - - path - - resource_type - - is_oauth - - is_linked - - is_refreshed - - ResourceType: - type: object - properties: - workspace_id: - type: string - name: - type: string - schema: {} - description: - type: string - created_by: - type: string - edited_at: - type: string - format: date-time - format_extension: - type: string - required: - - name - - EditResourceType: - type: object - properties: - schema: {} - description: - type: string - - Schedule: - type: object - properties: - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - schedule: - type: string - timezone: - type: string - enabled: - type: boolean - script_path: - type: string - is_flow: - type: boolean - args: - $ref: "#/components/schemas/ScriptArgs" - extra_perms: - type: object - additionalProperties: - type: boolean - email: - type: string - error: - type: string - on_failure: - # a reference to a script path, flow path, or webhook (script/, flow/) - type: string - on_failure_times: - type: number - on_failure_exact: - type: boolean - on_failure_extra_args: - $ref: "#/components/schemas/ScriptArgs" - on_recovery: - type: string - on_recovery_times: - type: number - on_recovery_extra_args: - $ref: "#/components/schemas/ScriptArgs" - on_success: - type: string - on_success_extra_args: - $ref: "#/components/schemas/ScriptArgs" - ws_error_handler_muted: - type: boolean - retry: - $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" - summary: - type: string - no_flow_overlap: - type: boolean - tag: - type: string - paused_until: - type: string - format: date-time - required: - - path - - edited_by - - edited_at - - schedule - - script_path - - timezone - - extra_perms - - is_flow - - enabled - - email - - ScheduleWJobs: - allOf: - - $ref: "#/components/schemas/Schedule" - - type: object - properties: - jobs: - type: array - items: - type: object - properties: - id: - type: string - success: - type: boolean - duration_ms: - type: number - required: - - id - - success - - duration_ms - - NewSchedule: - type: object - properties: - path: - type: string - schedule: - type: string - timezone: - type: string - script_path: - type: string - is_flow: - type: boolean - args: - $ref: "#/components/schemas/ScriptArgs" - enabled: - type: boolean - on_failure: - # a reference to a script path, flow path, or webhook (script/, flow/) - type: string - on_failure_times: - type: number - on_failure_exact: - type: boolean - on_failure_extra_args: - $ref: "#/components/schemas/ScriptArgs" - on_recovery: - type: string - on_recovery_times: - type: number - on_recovery_extra_args: - $ref: "#/components/schemas/ScriptArgs" - on_success: - type: string - on_success_extra_args: - $ref: "#/components/schemas/ScriptArgs" - ws_error_handler_muted: - type: boolean - retry: - $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" - no_flow_overlap: - type: boolean - summary: - type: string - tag: - type: string - paused_until: - type: string - format: date-time - required: - - path - - schedule - - timezone - - script_path - - is_flow - - args - - EditSchedule: - type: object - properties: - schedule: - type: string - timezone: - type: string - args: - $ref: "#/components/schemas/ScriptArgs" - on_failure: - # a reference to a script path, flow path, or webhook (script/, flow/) - type: string - on_failure_times: - type: number - on_failure_exact: - type: boolean - on_failure_extra_args: - $ref: "#/components/schemas/ScriptArgs" - on_recovery: - type: string - on_recovery_times: - type: number - on_recovery_extra_args: - $ref: "#/components/schemas/ScriptArgs" - on_success: - type: string - on_success_extra_args: - $ref: "#/components/schemas/ScriptArgs" - ws_error_handler_muted: - type: boolean - retry: - $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" - no_flow_overlap: - type: boolean - summary: - type: string - tag: - type: string - paused_until: - type: string - format: date-time - required: - - schedule - - timezone - - script_path - - is_flow - - args - - TriggersExtraProperty: - type: object - properties: - email: - type: string - extra_perms: - type: object - additionalProperties: - type: boolean - workspace_id: - type: string - - HttpTrigger: - allOf: - - $ref: '#/components/schemas/TriggersExtraProperty' - type: object - properties: - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - script_path: - type: string - route_path: - type: string - static_asset_config: - type: object - properties: - s3: - type: string - storage: - type: string - filename: - type: string - required: - - s3 - is_flow: - type: boolean - http_method: - type: string - enum: - - get - - post - - put - - delete - - patch - is_async: - type: boolean - requires_auth: - type: boolean - - required: - - path - - edited_by - - edited_at - - script_path - - route_path - - extra_perms - - is_flow - - email - - workspace_id - - is_async - - requires_auth - - http_method - - NewHttpTrigger: - type: object - properties: - path: - type: string - script_path: - type: string - route_path: - type: string - static_asset_config: - type: object - properties: - s3: - type: string - storage: - type: string - filename: - type: string - required: - - s3 - is_flow: - type: boolean - http_method: - type: string - enum: - - get - - post - - put - - delete - - patch - is_async: - type: boolean - requires_auth: - type: boolean - - required: - - path - - script_path - - route_path - - is_flow - - is_async - - requires_auth - - http_method - - EditHttpTrigger: - type: object - properties: - path: - type: string - script_path: - type: string - route_path: - type: string - static_asset_config: - type: object - properties: - s3: - type: string - storage: - type: string - filename: - type: string - required: - - s3 - is_flow: - type: boolean - http_method: - type: string - enum: - - get - - post - - put - - delete - - patch - is_async: - type: boolean - requires_auth: - type: boolean - required: - - path - - script_path - - is_flow - - kind - - is_async - - requires_auth - - http_method - - TriggersCount: - type: object - properties: - primary_schedule: - type: object - properties: - schedule: - type: string - schedule_count: - type: number - http_routes_count: - type: number - webhook_count: - type: number - email_count: - type: number - websocket_count: - type: number - database_count: - type: number - - WebsocketTrigger: - allOf: - - $ref: '#/components/schemas/TriggersExtraProperty' - type: object - properties: - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - script_path: - type: string - url: - type: string - is_flow: - type: boolean - server_id: - type: string - last_server_ping: - type: string - format: date-time - error: - type: string - enabled: - type: boolean - filters: - type: array - items: - type: object - properties: - key: - type: string - value: {} - required: - - key - - value - initial_messages: - type: array - items: - $ref: "#/components/schemas/WebsocketTriggerInitialMessage" - url_runnable_args: - $ref: "#/components/schemas/ScriptArgs" - - required: - - path - - edited_by - - edited_at - - script_path - - url - - extra_perms - - is_flow - - email - - workspace_id - - enabled - - filters - - initial_messages - - url_runnable_args - - NewWebsocketTrigger: - type: object - properties: - path: - type: string - script_path: - type: string - is_flow: - type: boolean - url: - type: string - enabled: - type: boolean - filters: - type: array - items: - type: object - properties: - key: - type: string - value: {} - required: - - key - - value - initial_messages: - type: array - items: - $ref: "#/components/schemas/WebsocketTriggerInitialMessage" - url_runnable_args: - $ref: "#/components/schemas/ScriptArgs" - - required: - - path - - script_path - - url - - is_flow - - filters - - initial_messages - - url_runnable_args - - EditWebsocketTrigger: - type: object - properties: - url: - type: string - path: - type: string - script_path: - type: string - is_flow: - type: boolean - filters: - type: array - items: - type: object - properties: - key: - type: string - value: {} - required: - - key - - value - initial_messages: - type: array - items: - $ref: "#/components/schemas/WebsocketTriggerInitialMessage" - url_runnable_args: - $ref: "#/components/schemas/ScriptArgs" - - required: - - path - - script_path - - url - - is_flow - - filters - - initial_messages - - url_runnable_args - - WebsocketTriggerInitialMessage: - anyOf: - - type: object - properties: - raw_message: - type: string - required: - - raw_message - - type: object - properties: - runnable_result: - type: object - properties: - path: - type: string - args: - $ref: "#/components/schemas/ScriptArgs" - is_flow: - type: boolean - required: - - path - - args - - is_flow - required: - - runnable_result - - TableToTrack: - type: object - properties: - table_name: - type: string - columns_name: - type: array - items: - type: string - required: - - table_name - - DatabaseToTrack: - type: object - properties: - username: - type: string - password: - type: string - host: - type: string - port: - type: number - required: - - username - - host - - port - - DatabaseToTrackWithoutPass: - allOf: - $ref: "#/components/schemas/DatabaseToTrack" - type: object - properties: - username: - type: string - host: - type: string - port: - type: number - required: - - username - - host - - port - - DatabaseTrigger: - allOf: - - $ref: '#/components/schemas/TriggersExtraProperty' - type: object - properties: - path: - type: string - script_path: - type: string - is_flow: - type: boolean - enabled: - type: boolean - database: - $ref: "#/components/schemas/DatabaseToTrackWithoutPass" - required: - - username - - host - - port - table_to_track: - type: array - items: - $ref: "#/components/schemas/TableToTrack" - - required: - - path - - script_path - - is_flow - - enabled - - database - - NewDatabaseTrigger: - type: object - properties: - path: - type: string - script_path: - type: string - is_flow: - type: boolean - enabled: - type: boolean - database: - $ref: "#/components/schemas/DatabaseToTrack" - required: - - username - - host - - port - table_to_track: - type: array - items: - $ref: "#/components/schemas/TableToTrack" - - required: - - path - - script_path - - is_flow - - enabled - - database - - EditDatabaseTrigger: - type: object - properties: - path: - type: string - script_path: - type: string - is_flow: - type: boolean - enabled: - type: boolean - database: - $ref: "#/components/schemas/DatabaseToTrack" - required: - - username - - host - - port - table_to_track: - type: array - items: - $ref: "#/components/schemas/TableToTrack" - - Group: - type: object - properties: - name: - type: string - summary: - type: string - members: - type: array - items: - type: string - extra_perms: - type: object - additionalProperties: - type: boolean - required: - - name - - InstanceGroup: - type: object - properties: - name: - type: string - summary: - type: string - emails: - type: array - items: - type: string - required: - - name - - Folder: - type: object - properties: - name: - type: string - owners: - type: array - items: - type: string - extra_perms: - type: object - additionalProperties: - type: boolean - summary: - type: string - created_by: - type: string - edited_at: - type: string - format: date-time - required: - - name - - owners - - extra_perms - - WorkerPing: - type: object - properties: - worker: - type: string - worker_instance: - type: string - last_ping: - type: number - started_at: - type: string - format: date-time - ip: - type: string - jobs_executed: - type: integer - custom_tags: - type: array - items: - type: string - worker_group: - type: string - wm_version: - type: string - last_job_id: - type: string - last_job_workspace_id: - type: string - occupancy_rate: - type: number - occupancy_rate_15s: - type: number - occupancy_rate_5m: - type: number - occupancy_rate_30m: - type: number - memory: - type: number - vcpus: - type: number - memory_usage: - type: number - wm_memory_usage: - type: number - required: - - worker - - worker_instance - - ping_at - - started_at - - ip - - jobs_executed - - worker_group - - wm_version - UserWorkspaceList: - type: object - properties: - email: - type: string - workspaces: - type: array - items: - type: object - properties: - id: - type: string - name: - type: string - username: - type: string - required: - - id - - name - - username - required: - - email - - workspaces - - CreateWorkspace: - type: object - properties: - id: - type: string - name: - type: string - username: - type: string - required: - - id - - name - - Workspace: - type: object - properties: - id: - type: string - name: - type: string - owner: - type: string - domain: - type: string - required: - - id - - name - - owner - - WorkspaceInvite: - type: object - properties: - workspace_id: - type: string - email: - type: string - is_admin: - type: boolean - operator: - type: boolean - required: - - workspace_id - - email - - is_admin - - operator - - GlobalUserInfo: - type: object - properties: - email: - type: string - login_type: - type: string - enum: ["password", "github"] - super_admin: - type: boolean - verified: - type: boolean - name: - type: string - company: - type: string - username: - type: string - operator_only: - type: boolean - - required: - - email - - login_type - - super_admin - - verified - - Flow: - allOf: - - $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" - - $ref: "#/components/schemas/FlowMetadata" - - ExtraPerms: - type: object - additionalProperties: - type: boolean - - FlowMetadata: - type: object - properties: - workspace_id: - type: string - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - archived: - type: boolean - extra_perms: - $ref: "#/components/schemas/ExtraPerms" - starred: - type: boolean - draft_only: - type: boolean - tag: - type: string - ws_error_handler_muted: - type: boolean - priority: - type: integer - dedicated_worker: - type: boolean - timeout: - type: number - visible_to_runner_only: - type: boolean - required: - - path - - edited_by - - edited_at - - archived - - extra_perms - - OpenFlowWPath: - allOf: - - $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" - - type: object - properties: - path: - type: string - tag: - type: string - ws_error_handler_muted: - type: boolean - priority: - type: integer - dedicated_worker: - type: boolean - timeout: - type: number - visible_to_runner_only: - type: boolean - required: - - path - - FlowPreview: - type: object - properties: - value: - $ref: "../../openflow.openapi.yaml#/components/schemas/FlowValue" - path: - type: string - args: - $ref: "#/components/schemas/ScriptArgs" - tag: - type: string - restarted_from: - $ref: "#/components/schemas/RestartedFrom" - - required: - - value - - content - - args - - RestartedFrom: - type: object - properties: - flow_job_id: - type: string - format: uuid - step_id: - type: string - branch_or_iteration_n: - type: integer - - Policy: - type: object - properties: - triggerables: - type: object - additionalProperties: - type: object - triggerables_v2: - type: object - additionalProperties: - type: object - s3_inputs: - type: array - items: - type: object - execution_mode: - type: string - enum: [viewer, publisher, anonymous] - on_behalf_of: - type: string - on_behalf_of_email: - type: string - - ListableApp: - type: object - properties: - id: - type: integer - workspace_id: - type: string - path: - type: string - summary: - type: string - version: - type: integer - extra_perms: - type: object - additionalProperties: - type: boolean - starred: - type: boolean - edited_at: - type: string - format: date-time - execution_mode: - type: string - enum: [viewer, publisher, anonymous] - required: - - id - - workspace_id - - path - - summary - - version - - extra_perms - - edited_at - - execution_mode - - ListableRawApp: - type: object - properties: - workspace_id: - type: string - path: - type: string - summary: - type: string - extra_perms: - type: object - additionalProperties: - type: boolean - starred: - type: boolean - version: - type: number - edited_at: - type: string - format: date-time - required: - - workspace_id - - path - - summary - - extra_perms - - version - - edited_at - - AppWithLastVersion: - type: object - properties: - id: - type: integer - workspace_id: - type: string - path: - type: string - summary: - type: string - versions: - type: array - items: - type: integer - created_by: - type: string - created_at: - type: string - format: date-time - value: - type: object - policy: - $ref: "#/components/schemas/Policy" - execution_mode: - type: string - enum: [viewer, publisher, anonymous] - extra_perms: - type: object - additionalProperties: - type: boolean - required: - - id - - workspace_id - - path - - summary - - versions - - created_by - - created_at - - value - - policy - - execution_mode - - extra_perms - - AppWithLastVersionWDraft: - allOf: - - $ref: "#/components/schemas/AppWithLastVersion" - - type: object - properties: - draft_only: - type: boolean - draft: {} - - AppHistory: - type: object - properties: - version: - type: integer - deployment_msg: - type: string - required: - - version - - FlowVersion: - type: object - properties: - id: - type: integer - created_at: - type: string - format: date-time - deployment_msg: - type: string - required: - - id - - created_at - - SlackToken: - type: object - properties: - access_token: - type: string - team_id: - type: string - team_name: - type: string - bot: - type: object - properties: - bot_access_token: - type: string - required: - - access_token - - team_id - - team_name - - bot - - TokenResponse: - type: object - properties: - access_token: - type: string - expires_in: - type: integer - refresh_token: - type: string - scope: - type: array - items: - type: string - - required: - - access_token - - HubScriptKind: - name: kind - schema: - type: string - enum: [script, failure, trigger, approval] - - PolarsClientKwargs: - type: object - properties: - region_name: - type: string - required: - - region_name - - LargeFileStorage: - type: object - properties: - type: - type: string - enum: - [ - "S3Storage", - "AzureBlobStorage", - "AzureWorkloadIdentity", - "S3AwsOidc", - ] - s3_resource_path: - type: string - azure_blob_resource_path: - type: string - public_resource: - type: boolean - secondary_storage: - type: object - additionalProperties: - type: object - properties: - type: - type: string - enum: - [ - "S3Storage", - "AzureBlobStorage", - "AzureWorkloadIdentity", - "S3AwsOidc", - ] - s3_resource_path: - type: string - azure_blob_resource_path: - type: string - public_resource: - type: boolean - - WindmillLargeFile: - type: object - properties: - s3: - type: string - required: - - s3 - - WindmillFileMetadata: - type: object - properties: - mime_type: - type: string - size_in_bytes: - type: integer - last_modified: - type: string - format: date-time - expires: - type: string - format: date-time - version_id: - type: string - - WindmillFilePreview: - type: object - properties: - msg: - type: string - content: - type: string - content_type: - type: string - enum: ["RawText", "Csv", "Parquet", "Unknown"] - required: - - content_type - - S3Resource: - type: object - properties: - bucket: - type: string - region: - type: string - endPoint: - type: string - useSSL: - type: boolean - accessKey: - type: string - secretKey: - type: string - pathStyle: - type: boolean - required: - - bucket - - region - - endPoint - - useSSL - - pathStyle - - WorkspaceGitSyncSettings: - type: object - properties: - include_path: - type: array - items: - type: string - include_type: - type: array - items: - type: string - enum: - - script - - flow - - app - - folder - - resource - - variable - - secret - - resourcetype - - schedule - - user - - group - repositories: - type: array - items: - $ref: "#/components/schemas/GitRepositorySettings" - - WorkspaceDeployUISettings: - type: object - properties: - include_path: - type: array - items: - type: string - include_type: - type: array - items: - type: string - enum: - - script - - flow - - app - - resource - - variable - - secret - - WorkspaceDefaultScripts: - type: object - properties: - order: - type: array - items: - type: string - hidden: - type: array - items: - type: string - default_script_content: - additionalProperties: - type: string - - GitRepositorySettings: - type: object - properties: - script_path: - type: string - git_repo_resource_path: - type: string - use_individual_branch: - type: boolean - group_by_folder: - type: boolean - exclude_types_override: - type: array - items: - type: string - enum: - - script - - flow - - app - - folder - - resource - - variable - - secret - - resourcetype - - schedule - - user - - group - required: - - script_path - - git_repo_resource_path - - UploadFilePart: - type: object - properties: - part_number: - type: integer - tag: - type: string - required: - - part_number - - tag - - MetricMetadata: - type: object - properties: - id: - type: string - name: - type: string - required: - - id - - ScalarMetric: - type: object - properties: - metric_id: - type: string - value: - type: number - required: - - id - - value - - TimeseriesMetric: - type: object - properties: - metric_id: - type: string - values: - type: array - items: - $ref: "#/components/schemas/MetricDataPoint" - required: - - id - - values - - MetricDataPoint: - type: object - properties: - timestamp: - type: string - format: date-time - value: - type: number - required: - - timestamp - - value - - RawScriptForDependencies: - type: object - properties: - raw_code: - type: string - path: - type: string - language: - type: string - enum: - [ - python3, - deno, - go, - bash, - powershell, - postgresql, - mysql, - bigquery, - snowflake, - mssql, - graphql, - nativets, - bun, - php, - rust, - ansible, - ] - required: - - raw_code - - path - - language - - ConcurrencyGroup: - type: object - properties: - concurrency_key: - type: string - total_running: - type: number - required: - - concurrency_key - - total_running - - ExtendedJobs: - type: object - properties: - jobs: - type: array - items: - $ref: "#/components/schemas/Job" - obscured_jobs: - type: array - items: - $ref : "#/components/schemas/ObscuredJob" - omitted_obscured_jobs: - description: "Obscured jobs omitted for security because of too specific filtering" - type: boolean - required: - - jobs - - obscured_jobs - - ExportedUser: - type: object - properties: - email: - type: string - password_hash: - type: string - super_admin: - type: boolean - verified: - type: boolean - name: - type: string - company: - type: string - first_time_user: - type: boolean - username: - type: string - required: - - email - - super_admin - - verified - - first_time_user - - GlobalSetting: - type: object - properties: - name: - type: string - value: - type: object - required: - - name - - value - - Config: - type: object - properties: - name: - type: string - config: - type: object - required: - - name - - ExportedInstanceGroup: - type: object - properties: - name: - type: string - summary: - type: string - emails: - type: array - items: - type: string - id: - type: string - scim_display_name: - type: string - external_id: - type: string - required: - - name - - JobSearchHit: - type: object - properties: - dancer: - type: string - - LogSearchHit: - type: object - properties: - dancer: - type: string - - AutoscalingEvent: - type: object - properties: - id: - type: integer - format: int64 - worker_group: - type: string - event_type: - type: string - desired_workers: - type: integer - reason: - type: string - applied_at: - type: string - format: date-time - - CriticalAlert: - type: object - properties: - id: - type: integer - description: Unique identifier for the alert - alert_type: - type: string - description: Type of alert (e.g., critical_error) - message: - type: string - description: The message content of the alert - created_at: - type: string - format: date-time - description: Time when the alert was created - acknowledged: - type: boolean - nullable: true - description: Acknowledgment status of the alert, can be true, false, or null if not set - workspace_id: - type: string - nullable: true - description: Workspace id if the alert is in the scope of a workspace diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 7ed3fe6ce2057..e273b699912b4 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -12346,6 +12346,13 @@ components: - enabled - database + TransactionType: + type: string + enum: + - Insert + - Update + - Delete + NewDatabaseTrigger: type: object properties: @@ -12358,22 +12365,20 @@ components: enabled: type: boolean database: - $ref: "#/components/schemas/DatabaseToTrack" - required: - - username - - host - - port + type: string table_to_track: type: array items: $ref: "#/components/schemas/TableToTrack" - + transaction_type: + $ref: "#/components/schemas/TransactionType" required: - path - script_path - is_flow - enabled - database + - transaction_type EditDatabaseTrigger: type: object diff --git a/backend/windmill-api/src/database_triggers.rs b/backend/windmill-api/src/database_triggers.rs index b3aefc1e6869d..f552e6c9a5fa4 100644 --- a/backend/windmill-api/src/database_triggers.rs +++ b/backend/windmill-api/src/database_triggers.rs @@ -6,8 +6,8 @@ use axum::{ use http::StatusCode; use itertools::Itertools; use rand::seq::SliceRandom; -use serde::{Deserialize, Serialize}; - +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::value::to_value; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::FromRow; use windmill_audit::{audit_ee::audit_log, ActionKind}; @@ -21,6 +21,14 @@ use windmill_common::{ use crate::db::{ApiAuthed, DB}; +#[derive(Clone, Debug, sqlx::Type, Deserialize, Serialize)] +#[sqlx(type_name = "transaction_type")] +enum TransactionType { + Insert, + Update, + Delete, +} + #[derive(FromRow, Serialize, Deserialize)] struct Database { username: String, @@ -42,7 +50,7 @@ impl Database { } } -#[derive(FromRow, Serialize, Deserialize)] +#[derive(FromRow, Serialize, Deserialize, Debug)] struct TableToTrack { table_name: String, columns_name: Option>, @@ -57,10 +65,12 @@ struct EditDatabaseTrigger { table_to_track: Option>, } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Debug)] struct NewDatabaseTrigger { path: String, + #[serde(deserialize_with = "check_if_valid_transaction_type")] + transaction_type: String, script_path: String, is_flow: bool, enabled: bool, @@ -68,9 +78,24 @@ struct NewDatabaseTrigger { table_to_track: Option>, } +fn check_if_valid_transaction_type<'de, D>(transaction_type: D) -> std::result::Result +where + D: Deserializer<'de>, +{ + let transaction_type = String::deserialize(transaction_type)?; + match transaction_type.as_str() { + "Insert" | "Update" | "Delete" => Ok(transaction_type), + _ => Err(serde::de::Error::custom( + "Only the following transaction types are allowed: Insert, Update and Delete" + .to_string(), + )), + } +} + #[derive(FromRow, Deserialize, Serialize)] struct DatabaseTrigger { workspace_id: String, + transaction_type: TransactionType, path: String, script_path: String, is_flow: bool, @@ -106,13 +131,21 @@ async fn create_database_trigger( Path(w_id): Path, Json(new_database_trigger): Json, ) -> error::Result<(StatusCode, String)> { - let NewDatabaseTrigger { database, table_to_track, path, script_path, enabled, is_flow } = - new_database_trigger; + let NewDatabaseTrigger { + database, + table_to_track, + path, + script_path, + enabled, + is_flow, + transaction_type, + } = new_database_trigger; if *CLOUD_HOSTED { return Err(error::Error::BadRequest( "Database triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), )); } + let table_to_track = table_to_track.map(|table_to_track| { table_to_track .into_iter() @@ -121,10 +154,11 @@ async fn create_database_trigger( }); let mut tx = user_db.begin(&authed).await?; - sqlx::query_as::<_, DatabaseTrigger>("INSERT INTO database_trigger (workspace_id, path, script_path, is_flow, email, enabled, database, table_to_track, edited_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now()) RETURNING *") + sqlx::query_as::<_, DatabaseTrigger>("INSERT INTO database_trigger (workspace_id, path, script_path, transaction_type, is_flow, email, enabled, database, table_to_track, edited_by, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now()) RETURNING *") .bind(&w_id) .bind(&path) .bind(script_path) + .bind(transaction_type) .bind(is_flow) .bind(&authed.email) .bind(enabled) @@ -219,12 +253,10 @@ async fn update_database_trigger( database_trigger; let mut tx = user_db.begin(&authed).await?; - let table_to_track = table_to_track.map(|table_to_track| { - table_to_track - .into_iter() - .map(sqlx::types::Json) - .collect_vec() - }); + let table_to_track = table_to_track + .into_iter() + .map(|table| to_value(table).unwrap()) + .collect_vec(); sqlx::query!( "UPDATE database_trigger SET script_path = $1, path = $2, is_flow = $3, edited_by = $4, email = $5, database = $6, table_to_track = $7, edited_at = now(), error = NULL @@ -235,7 +267,7 @@ async fn update_database_trigger( &authed.username, &authed.email, database, - table_to_track, + table_to_track.as_slice(), w_id, workspace_path, ) diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index 535503a332b36..e6e1dbba8840c 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -5,7 +5,12 @@ import Path from '$lib/components/Path.svelte' import Required from '$lib/components/Required.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import { DatabaseTriggerService, type DatabaseToTrack, type TableToTrack } from '$lib/gen' + import { + DatabaseTriggerService, + type DatabaseToTrack, + type TableToTrack, + type TransactionType + } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, sendUserToast } from '$lib/utils' import { createEventDispatcher } from 'svelte' @@ -28,13 +33,13 @@ let fixedScriptPath = '' let path: string = '' let pathError = '' - let urlError = '' let enabled = false let database: DatabaseToTrack let tableToTrack: TableToTrack[] | undefined let dirtyPath = false let can_write = true let drawerLoading = true + let database_path = '' const dispatch = createEventDispatcher() @@ -102,7 +107,12 @@ script_path, is_flow, database, - table_to_track: tableToTrack + table_to_track: tables.map((table) => { + return { + table_name: table, + columns_name: [] + } + }) } }) sendUserToast(`Route ${path} updated`) @@ -110,12 +120,18 @@ await DatabaseTriggerService.createDatabaseTrigger({ workspace: $workspaceStore!, requestBody: { + transaction_type: selected, path, script_path, is_flow, enabled: true, - database, - table_to_track: tableToTrack + database: database_path, + table_to_track: tables.map((table) => { + return { + table_name: table, + columns_name: [] + } + }) } }) sendUserToast(`Route ${path} created`) @@ -127,7 +143,7 @@ drawer.closeDrawer() } - let selected: 'insert' | 'updated' | 'delete' = 'insert' + let selected: TransactionType = 'Insert' let tables: string[] = [] @@ -163,7 +179,10 @@ {/if} + + {/each} + +
+ +
+ {/if} diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte index e66ca86e9d0ba..fc82d5bb39191 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggersPanel.svelte @@ -32,7 +32,7 @@ ).map((x) => { return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) - $triggersCount = { ...($triggersCount ?? {}), websocket_count: databaseTriggers?.length } + $triggersCount = { ...($triggersCount ?? {}), database_count: databaseTriggers?.length } } catch (e) { console.error('impossible to load Database triggers', e) } diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index 1d3ef9eb596f4..03ec57e2c660b 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -190,15 +190,20 @@ async function loadUsedTriggerKinds() { let usedKinds: string[] = [] - const { http_routes_used, websocket_used } = await WorkspaceService.getUsedTriggers({ - workspace: $workspaceStore ?? '' - }) + const { http_routes_used, websocket_used, database_used } = + await WorkspaceService.getUsedTriggers({ + workspace: $workspaceStore ?? '' + }) if (http_routes_used) { usedKinds.push('http') } if (websocket_used) { usedKinds.push('ws') } + + if (database_used) { + usedKinds.push('database') + } $usedTriggerKinds = usedKinds } From 18b96cccc539d4bb5f87fb1c929546a8639a2d9b Mon Sep 17 00:00:00 2001 From: dieriba Date: Mon, 9 Dec 2024 00:37:22 +0100 Subject: [PATCH 22/50] feat: :construction: --- .../src/database_triggers/converter.rs | 256 ++++++++++++++++++ .../windmill-api/src/database_triggers/mod.rs | 10 +- .../src/database_triggers/relation.rs | 79 ++++++ .../database_triggers/replication_message.rs | 219 +++++++++------ .../src/database_triggers/trigger.rs | 34 ++- 5 files changed, 504 insertions(+), 94 deletions(-) create mode 100644 backend/windmill-api/src/database_triggers/converter.rs create mode 100644 backend/windmill-api/src/database_triggers/relation.rs diff --git a/backend/windmill-api/src/database_triggers/converter.rs b/backend/windmill-api/src/database_triggers/converter.rs new file mode 100644 index 0000000000000..787edaa220d59 --- /dev/null +++ b/backend/windmill-api/src/database_triggers/converter.rs @@ -0,0 +1,256 @@ +use core::str; +use std::num::{ParseFloatError, ParseIntError}; + +use bigdecimal::ParseBigDecimalError; +use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Utc}; +use thiserror::Error; +use tokio_postgres::types::Type; +use uuid::Uuid; + +use crate::conversions::{bool::parse_bool, hex}; + +use super::{bool::ParseBoolError, hex::ByteaHexParseError, numeric::PgNumeric, ArrayCell, Cell}; + +#[derive(Debug, Error)] +pub enum ConverterError { + #[error("invalid bool value")] + InvalidBool(#[from] ParseBoolError), + + #[error("invalid int value")] + InvalidInt(#[from] ParseIntError), + + #[error("invalid float value")] + InvalidFloat(#[from] ParseFloatError), + + #[error("invalid numeric: {0}")] + InvalidNumeric(#[from] ParseBigDecimalError), + + #[error("invalid bytea: {0}")] + InvalidBytea(#[from] ByteaHexParseError), + + #[error("invalid uuid: {0}")] + InvalidUuid(#[from] uuid::Error), + + #[error("invalid json: {0}")] + InvalidJson(#[from] serde_json::Error), + + #[error("invalid timestamp: {0} ")] + InvalidTimestamp(#[from] chrono::ParseError), + + #[error("invalid array: {0}")] + InvalidArray(#[from] ArrayParseError), +} + +pub struct Converter; + +#[derive(Debug, Error)] +pub enum ArrayParseError { + #[error("input too short")] + InputTooShort, + + #[error("missing braces")] + MissingBraces, +} + +impl Converter { + pub fn try_from_str(typ: Option<&Type>, str: &str) -> Result { + match *typ { + Type::BOOL => Ok(Cell::Bool(parse_bool(str)?)), + Type::BOOL_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(parse_bool(str)?)), ArrayCell::Bool) + } + Type::CHAR | Type::BPCHAR | Type::VARCHAR | Type::NAME | Type::TEXT => { + Ok(Cell::String(str.to_string())) + } + Type::CHAR_ARRAY + | Type::BPCHAR_ARRAY + | Type::VARCHAR_ARRAY + | Type::NAME_ARRAY + | Type::TEXT_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.to_string())), ArrayCell::String) + } + Type::INT2 => Ok(Cell::I16(str.parse()?)), + Type::INT2_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::I16) + } + Type::INT4 => Ok(Cell::I32(str.parse()?)), + Type::INT4_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::I32) + } + Type::INT8 => Ok(Cell::I64(str.parse()?)), + Type::INT8_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::I64) + } + Type::FLOAT4 => Ok(Cell::F32(str.parse()?)), + Type::FLOAT4_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::F32) + } + Type::FLOAT8 => Ok(Cell::F64(str.parse()?)), + Type::FLOAT8_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::F64) + } + Type::NUMERIC => Ok(Cell::Numeric(str.parse()?)), + Type::NUMERIC_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::Numeric) + } + Type::BYTEA => Ok(Cell::Bytes(hex::from_bytea_hex(str)?)), + Type::BYTEA_ARRAY => Converter::parse_array( + str, + |str| Ok(Some(hex::from_bytea_hex(str)?)), + ArrayCell::Bytes, + ), + Type::DATE => { + let val = NaiveDate::parse_from_str(str, "%Y-%m-%d")?; + Ok(Cell::Date(val)) + } + Type::DATE_ARRAY => Converter::parse_array( + str, + |str| Ok(Some(NaiveDate::parse_from_str(str, "%Y-%m-%d")?)), + ArrayCell::Date, + ), + Type::TIME => { + let val = NaiveTime::parse_from_str(str, "%H:%M:%S%.f")?; + Ok(Cell::Time(val)) + } + Type::TIME_ARRAY => Converter::parse_array( + str, + |str| Ok(Some(NaiveTime::parse_from_str(str, "%H:%M:%S%.f")?)), + ArrayCell::Time, + ), + Type::TIMESTAMP => { + let val = NaiveDateTime::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f")?; + Ok(Cell::TimeStamp(val)) + } + Type::TIMESTAMP_ARRAY => Converter::parse_array( + str, + |str| { + Ok(Some(NaiveDateTime::parse_from_str( + str, + "%Y-%m-%d %H:%M:%S%.f", + )?)) + }, + ArrayCell::TimeStamp, + ), + Type::TIMESTAMPTZ => { + let val = + match DateTime::::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f%#z") { + Ok(val) => val, + Err(_) => { + DateTime::::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f%:z")? + } + }; + Ok(Cell::TimeStampTz(val.into())) + } + Type::TIMESTAMPTZ_ARRAY => { + match Converter::parse_array( + str, + |str| { + Ok(Some( + DateTime::::parse_from_str( + str, + "%Y-%m-%d %H:%M:%S%.f%#z", + )? + .into(), + )) + }, + ArrayCell::TimeStampTz, + ) { + Ok(val) => Ok(val), + Err(_) => Converter::parse_array( + str, + |str| { + Ok(Some( + DateTime::::parse_from_str( + str, + "%Y-%m-%d %H:%M:%S%.f%:z", + )? + .into(), + )) + }, + ArrayCell::TimeStampTz, + ), + } + } + Type::UUID => { + let val = Uuid::parse_str(str)?; + Ok(Cell::Uuid(val)) + } + Type::UUID_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(Uuid::parse_str(str)?)), ArrayCell::Uuid) + } + Type::JSON | Type::JSONB => { + let val = serde_json::from_str(str)?; + Ok(Cell::Json(val)) + } + Type::JSON_ARRAY | Type::JSONB_ARRAY => Converter::parse_array( + str, + |str| Ok(Some(serde_json::from_str(str)?)), + ArrayCell::Json, + ), + Type::OID => { + let val: u32 = str.parse()?; + Ok(Cell::U32(val)) + } + Type::OID_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::U32) + } + _ => Ok(Cell::String(str.to_string())), + } + } + + fn parse_array(str: &str, mut parse: P, m: M) -> Result + where + P: FnMut(&str) -> Result, ConverterError>, + M: FnOnce(Vec>) -> ArrayCell, + { + if str.len() < 2 { + return Err(ArrayParseError::InputTooShort.into()); + } + + if !str.starts_with('{') || !str.ends_with('}') { + return Err(ArrayParseError::MissingBraces.into()); + } + + let mut res = vec![]; + let str = &str[1..(str.len() - 1)]; + let mut val_str = String::with_capacity(10); + let mut in_quotes = false; + let mut in_escape = false; + let mut chars = str.chars(); + let mut done = str.is_empty(); + + while !done { + loop { + match chars.next() { + Some(c) => match c { + c if in_escape => { + val_str.push(c); + in_escape = false; + } + '"' => in_quotes = !in_quotes, + '\\' => in_escape = true, + ',' if !in_quotes => { + break; + } + c => { + val_str.push(c); + } + }, + None => { + done = true; + break; + } + } + } + let val = if val_str.to_lowercase() == "null" { + None + } else { + parse(&val_str)? + }; + res.push(val); + val_str.clear(); + } + + Ok(Cell::Array(m(res))) + } +} diff --git a/backend/windmill-api/src/database_triggers/mod.rs b/backend/windmill-api/src/database_triggers/mod.rs index 6bf2173b3a272..a480e9fb1d12b 100644 --- a/backend/windmill-api/src/database_triggers/mod.rs +++ b/backend/windmill-api/src/database_triggers/mod.rs @@ -1,4 +1,5 @@ -use std::{collections::HashMap, fmt, io, str::Utf8Error, string::FromUtf8Error}; +use serde_json::value::RawValue; +use std::collections::HashMap; use crate::{ db::DB, @@ -19,8 +20,10 @@ use windmill_common::{db::UserDB, utils::StripPath}; use windmill_queue::PushArgsOwned; mod handler; +mod relation; mod replication_message; mod trigger; +mod converter; pub type SqlxJson = sqlx::types::Json; pub use trigger::start_database; @@ -37,12 +40,13 @@ pub fn workspaced_service() -> Router { } async fn run_job( + args: Option>>, db: &DB, rsmq: Option, trigger: &DatabaseTrigger, ) -> anyhow::Result<()> { - let args = PushArgsOwned { args: HashMap::new(), extra: None }; - + let args = PushArgsOwned { args: args.unwrap_or_default(), extra: None }; + println!("Args: {:#?}", args); let label_prefix = Some(format!("db-{}-", trigger.path)); let authed = fetch_api_authed( diff --git a/backend/windmill-api/src/database_triggers/relation.rs b/backend/windmill-api/src/database_triggers/relation.rs new file mode 100644 index 0000000000000..dd21ad5dec0c4 --- /dev/null +++ b/backend/windmill-api/src/database_triggers/relation.rs @@ -0,0 +1,79 @@ +use core::str; + +use serde_json::value::RawValue; +use std::{collections::HashMap, str::Utf8Error}; + +use super::{ + converter::{Converter, ConverterError}, + replication_message::{Columns, TupleData}, +}; +use rust_postgres::types::Oid; +use serde_json::Value; +use windmill_common::worker::to_raw_value; +#[derive(Debug, thiserror::Error)] +pub enum RelationConversionError { + #[error("Json error: {0}")] + FailConversionToJson(serde_json::Error), + + #[error("Could not find matching table")] + FailToFindMatchingTable, + + #[error("Missing Column {0}")] + MissingColumn(String), + + #[error("Binary data not supported")] + BinaryFormatNotSupported, + + #[error("decode error: {0}")] + FromBytes(#[from] ConverterError), + + #[error("invalid string value")] + InvalidStr(#[from] Utf8Error), +} + +pub struct RelationConverter(HashMap); + +impl RelationConverter { + pub fn new() -> Self { + Self(HashMap::new()) + } + + pub fn add_column(&mut self, oid: Oid, relation_body: Columns) { + self.0.insert(oid, relation_body); + } + + pub fn get_columns(&self, o_id: Oid) -> Result<&Columns, RelationConversionError> { + self.0 + .get(&o_id) + .ok_or(RelationConversionError::FailToFindMatchingTable) + } + + pub fn body_to_json( + &self, + to_decode: (Oid, Vec), + ) -> Result>, RelationConversionError> { + let (o_id, tuple_data) = to_decode; + let mut object: HashMap> = HashMap::new(); + let columns = self.get_columns(o_id)?; + + for (i, column) in columns.iter().enumerate() { + let value = match &tuple_data[i] { + TupleData::Null => to_raw_value::<&Option<()>>(&&None), + TupleData::UnchangedToast => { + return Err(RelationConversionError::BinaryFormatNotSupported) + } + TupleData::Binary(_) => { + return Err(RelationConversionError::BinaryFormatNotSupported) + } + TupleData::Text(bytes) => { + let str = str::from_utf8(&bytes)?; + Converter::try_from_str(column.type_o_id.as_ref(), str)? + } + }; + + object.insert(column.name.clone(), value); + } + + Ok(object) + } +} diff --git a/backend/windmill-api/src/database_triggers/replication_message.rs b/backend/windmill-api/src/database_triggers/replication_message.rs index 4a1dd5c438b77..5a7b753b398b6 100644 --- a/backend/windmill-api/src/database_triggers/replication_message.rs +++ b/backend/windmill-api/src/database_triggers/replication_message.rs @@ -8,12 +8,17 @@ use std::{ use byteorder::{BigEndian, ReadBytesExt}; use bytes::Bytes; use memchr::memchr; +use rust_postgres::types::{Kind, Oid, Type}; use thiserror::Error; use super::trigger::LogicalReplicationSettings; const PRIMARY_KEEPALIVE_BYTE: u8 = b'k'; const X_LOG_DATA_BYTE: u8 = b'w'; +pub trait IntoTupleData { + fn into_tuple_data(self) -> (Oid, Vec); +} + #[derive(Debug)] pub struct PrimaryKeepAliveBody { pub wal_end: u64, @@ -35,7 +40,6 @@ const TYPE_BYTE: u8 = b'Y'; const INSERT_BYTE: u8 = b'I'; const UPDATE_BYTE: u8 = b'U'; const DELETE_BYTE: u8 = b'D'; -const TRUNCATE_BYTE: u8 = b'T'; const TUPLE_NEW_BYTE: u8 = b'N'; const TUPLE_KEY_BYTE: u8 = b'K'; const TUPLE_OLD_BYTE: u8 = b'O'; @@ -88,80 +92,113 @@ pub enum ReplicaIdentity { pub struct Column { pub flags: i8, pub name: String, - pub type_id: i32, + pub type_o_id: Option, pub type_modifier: i32, } impl Column { - pub fn new(flags: i8, name: String, type_id: i32, type_modifier: i32) -> Self { - Self { flags, name, type_id, type_modifier } + pub fn new(flags: i8, name: String, type_o_id: Option, type_modifier: i32) -> Self { + Self { flags, name, type_o_id, type_modifier } } } +pub type Columns = Vec; + #[derive(Debug)] pub struct RelationBody { pub transaction_id: Option, - pub relation_id: i32, + pub o_id: Oid, pub namespace: String, pub name: String, pub replica_identity: ReplicaIdentity, - pub columns: Vec, + pub columns: Columns, } impl RelationBody { pub fn new( transaction_id: Option, - relation_id: i32, + o_id: Oid, namespace: String, name: String, replica_identity: ReplicaIdentity, - columns: Vec, + columns: Columns, ) -> Self { - Self { transaction_id, relation_id, namespace, name, replica_identity, columns } + Self { transaction_id, o_id, namespace, name, replica_identity, columns } } } #[derive(Debug)] pub struct TypeBody { - oid: i32, + o_id: Oid, namespace: String, name: String, } impl TypeBody { - pub fn new(oid: i32, namespace: String, name: String) -> TypeBody { - TypeBody { oid, namespace, name } + pub fn new(o_id: Oid, namespace: String, name: String) -> TypeBody { + TypeBody { o_id, namespace, name } } } #[derive(Debug)] pub struct InsertBody { pub transaction_id: Option, - pub o_id: i32, + pub o_id: Oid, pub tuple: Vec, } impl InsertBody { - pub fn new(transaction_id: Option, o_id: i32, tuple: Vec) -> Self { + pub fn new(transaction_id: Option, o_id: Oid, tuple: Vec) -> Self { Self { transaction_id, o_id, tuple } } } +impl IntoTupleData for InsertBody { + fn into_tuple_data(self) -> (Oid, Vec) { + (self.o_id, self.tuple) + } +} + #[derive(Debug)] pub struct UpdateBody { - rel_id: u32, - old_tuple: Option>, - key_tuple: Option>, - new_tuple: Vec, + transaction_id: Option, + pub o_id: Oid, + pub old_tuple: Option>, + pub key_tuple: Option>, + pub new_tuple: Vec, +} + +impl UpdateBody { + pub fn new( + transaction_id: Option, + o_id: Oid, + old_tuple: Option>, + key_tuple: Option>, + new_tuple: Vec, + ) -> Self { + Self { transaction_id, o_id, old_tuple, key_tuple, new_tuple } + } } #[derive(Debug)] pub struct DeleteBody { - rel_id: u32, + transaction_id: Option, + pub o_id: Oid, old_tuple: Option>, key_tuple: Option>, } +impl DeleteBody { + pub fn new( + transaction_id: Option, + o_id: Oid, + old_tuple: Option>, + key_tuple: Option>, + ) -> Self { + Self { transaction_id, o_id, old_tuple, key_tuple } + } +} + #[derive(Debug)] pub enum TupleData { Null, @@ -171,21 +208,21 @@ pub enum TupleData { } impl TupleData { - fn parse(mut buf: Buffer) -> Result, ConversionError> { - let number_of_columns = buf.read_i16::().map_err(ConversionError::Io)?; + fn parse(buf: &mut Buffer) -> Result, ConversionError> { + let number_of_columns = buf.read_i16::()?; let mut tuples = Vec::with_capacity(number_of_columns as usize); for _ in 0..number_of_columns { - let byte = buf.read_u8().map_err(ConversionError::Io)?; + let byte = buf.read_u8()?; let tuple_data = match byte { TUPLE_DATA_NULL_BYTE => TupleData::Null, TUPLE_DATA_TOAST_BYTE => TupleData::UnchangedToast, TUPLE_DATA_TEXT_BYTE => { - let len = buf.read_i32::().map_err(ConversionError::Io)?; + let len = buf.read_i32::()?; let bytes = buf.read_n_byte(len as usize); TupleData::Text(bytes) } TUPLE_DATA_BINARY_BYTE => { - let len = buf.read_i32::().map_err(ConversionError::Io)?; + let len = buf.read_i32::()?; let bytes = buf.read_n_byte(len as usize); TupleData::Text(bytes) } @@ -204,6 +241,13 @@ impl TupleData { } } +#[derive(Debug)] +pub enum TransactionBody { + Insert(InsertBody), + Update(UpdateBody), + Delete(DeleteBody), +} + #[non_exhaustive] #[derive(Debug)] pub enum LogicalReplicationMessage { @@ -212,8 +256,8 @@ pub enum LogicalReplicationMessage { Relation(RelationBody), Type(TypeBody), Insert(InsertBody), - Update, - Delete, + Update(UpdateBody), + Delete(DeleteBody), } #[derive(Debug)] @@ -227,9 +271,9 @@ pub struct XLogDataBody { #[derive(Error, Debug)] pub enum ConversionError { #[error("Error: {0}")] - Io(io::Error), + Io(#[from] io::Error), #[error("Utf8Error conversion: {0}")] - Utf8(Utf8Error), + Utf8(#[from] Utf8Error), } struct Buffer { @@ -251,9 +295,7 @@ impl Buffer { Some(pos) => { let start = self.idx; let end = start + pos; - let cstr = str::from_utf8(&self.bytes[start..end]) - .map_err(ConversionError::Utf8)? - .to_owned(); + let cstr = str::from_utf8(&self.bytes[start..end])?.to_owned(); self.idx = end + 1; Ok(cstr) } @@ -295,13 +337,13 @@ impl XLogDataBody { logical_replication_settings: &LogicalReplicationSettings, ) -> Result { let mut buf = Buffer::new(self.data.clone(), 0); - let byte = buf.read_u8().map_err(ConversionError::Io)?; + let byte = buf.read_u8()?; let logical_replication_message = match byte { BEGIN_BYTE => { - let last_lsn = buf.read_i64::().map_err(ConversionError::Io)?; - let timestamp = buf.read_i64::().map_err(ConversionError::Io)?; - let transaction_id = buf.read_i32::().map_err(ConversionError::Io)?; + let last_lsn = buf.read_i64::()?; + let timestamp = buf.read_i64::()?; + let transaction_id = buf.read_i32::()?; LogicalReplicationMessage::Begin(BeginBody::new( last_lsn, @@ -310,24 +352,24 @@ impl XLogDataBody { )) } COMMIT_BYTE => { - let flags = buf.read_i8().map_err(ConversionError::Io)?; - let commit_lsn = buf.read_u64::().map_err(ConversionError::Io)?; - let end_lsn = buf.read_u64::().map_err(ConversionError::Io)?; - let timestamp = buf.read_i64::().map_err(ConversionError::Io)?; + let flags = buf.read_i8()?; + let commit_lsn = buf.read_u64::()?; + let end_lsn = buf.read_u64::()?; + let timestamp = buf.read_i64::()?; LogicalReplicationMessage::Commit(CommitBody::new( flags, commit_lsn, end_lsn, timestamp, )) } RELATION_BYTE => { let transaction_id = match logical_replication_settings.streaming { - true => Some(buf.read_i32::().map_err(ConversionError::Io)?), + true => Some(buf.read_i32::()?), false => None, }; - let relation_id = buf.read_i32::().map_err(ConversionError::Io)?; + let o_id = buf.read_u32::()?; let namespace = buf.read_cstr()?; let name = buf.read_cstr()?; - let replica_identity = match buf.read_i8().map_err(ConversionError::Io)? { + let replica_identity = match buf.read_i8()? { REPLICA_IDENTITY_DEFAULT_BYTE => ReplicaIdentity::Default, REPLICA_IDENTITY_NOTHING_BYTE => ReplicaIdentity::Nothing, REPLICA_IDENTITY_FULL_BYTE => ReplicaIdentity::Full, @@ -340,22 +382,23 @@ impl XLogDataBody { } }; - let num_of_column = buf.read_i16::().map_err(ConversionError::Io)?; + let num_of_column = buf.read_i16::()?; let mut columns = Vec::with_capacity(num_of_column as usize); for _ in 0..num_of_column { - let flags = buf.read_i8().map_err(ConversionError::Io)?; + let flags = buf.read_i8()?; let name = buf.read_cstr()?; - let type_id = buf.read_i32::().map_err(ConversionError::Io)?; - let type_modifier = buf.read_i32::().map_err(ConversionError::Io)?; - let column = Column::new(flags, name, type_id, type_modifier); + let o_id = buf.read_u32::()?; + let type_modifier = buf.read_i32::()?; + let type_o_id = Type::from_oid(o_id); + let column = Column::new(flags, name, type_o_id, type_modifier); columns.push(column); } LogicalReplicationMessage::Relation(RelationBody::new( transaction_id, - relation_id, + o_id, namespace, name, replica_identity, @@ -363,22 +406,22 @@ impl XLogDataBody { )) } TYPE_BYTE => { - let oid = buf.read_i32::().map_err(ConversionError::Io)?; + let o_id = buf.read_u32::()?; let namespace = buf.read_cstr()?; let name = buf.read_cstr()?; - LogicalReplicationMessage::Type(TypeBody::new(oid, namespace, name)) + LogicalReplicationMessage::Type(TypeBody::new(o_id, namespace, name)) } INSERT_BYTE => { let transaction_id = match logical_replication_settings.streaming { - true => Some(buf.read_i32::().map_err(ConversionError::Io)?), + true => Some(buf.read_i32::()?), false => None, }; - let o_id = buf.read_i32::().map_err(ConversionError::Io)?; - let byte = buf.read_u8().map_err(ConversionError::Io)?; + let o_id = buf.read_u32::()?; + let byte = buf.read_u8()?; let tuple = match byte { - TUPLE_NEW_BYTE => TupleData::parse(buf)?, + TUPLE_NEW_BYTE => TupleData::parse(&mut buf)?, byte => { return Err(ConversionError::Io(io::Error::new( io::ErrorKind::InvalidInput, @@ -390,60 +433,78 @@ impl XLogDataBody { LogicalReplicationMessage::Insert(InsertBody::new(transaction_id, o_id, tuple)) } UPDATE_BYTE => { - /*let rel_id = buf.read_u32::()?; - let tag = buf.read_u8()?; + let transaction_id = match logical_replication_settings.streaming { + true => Some(buf.read_i32::()?), + false => None, + }; + let o_id = buf.read_u32::()?; + let byte = buf.read_u8()?; let mut key_tuple = None; let mut old_tuple = None; - let new_tuple = match tag { - TUPLE_NEW_BYTE => Tuple::parse(&mut buf)?, + let new_tuple = match byte { + TUPLE_NEW_BYTE => TupleData::parse(&mut buf)?, TUPLE_OLD_BYTE | TUPLE_KEY_BYTE => { - if tag == TUPLE_OLD_BYTE { - old_tuple = Some(Tuple::parse(&mut buf)?); + if byte == TUPLE_OLD_BYTE { + old_tuple = Some(TupleData::parse(&mut buf)?); } else { - key_tuple = Some(Tuple::parse(&mut buf)?); + key_tuple = Some(TupleData::parse(&mut buf)?); } - match buf.read_u8()? { - TUPLE_NEW_BYTE => Tuple::parse(&mut buf)?, - tag => { - return Err(io::Error::new( + TUPLE_NEW_BYTE => TupleData::parse(&mut buf)?, + byte => { + return Err(ConversionError::Io(io::Error::new( io::ErrorKind::InvalidInput, - format!("unexpected tuple tag `{}`", tag), - )); + format!("unexpected tuple byte `{}`", byte), + ))); } } } byte => { - return Err(io::Error::new( + return Err(ConversionError::Io(io::Error::new( io::ErrorKind::InvalidInput, - format!("unknown tuple tag `{}`", tag), - )); + format!("unknown tuple byte `{}`", byte), + ))); } - };*/ + }; - LogicalReplicationMessage::Update + LogicalReplicationMessage::Update(UpdateBody::new( + transaction_id, + o_id, + old_tuple, + key_tuple, + new_tuple, + )) } DELETE_BYTE => { - /*let rel_id = buf.read_u32::()?; + let transaction_id = match logical_replication_settings.streaming { + true => Some(buf.read_i32::()?), + false => None, + }; + let o_id = buf.read_u32::()?; let tag = buf.read_u8()?; let mut key_tuple = None; let mut old_tuple = None; match tag { - TUPLE_OLD_BYTE => old_tuple = Some(Tuple::parse(&mut buf)?), - TUPLE_KEY_BYTE => key_tuple = Some(Tuple::parse(&mut buf)?), + TUPLE_OLD_BYTE => old_tuple = Some(TupleData::parse(&mut buf)?), + TUPLE_KEY_BYTE => key_tuple = Some(TupleData::parse(&mut buf)?), tag => { - return Err(io::Error::new( + return Err(ConversionError::Io(io::Error::new( io::ErrorKind::InvalidInput, format!("unknown tuple tag `{}`", tag), - )); + ))); } - }*/ + } - LogicalReplicationMessage::Delete + LogicalReplicationMessage::Delete(DeleteBody::new( + transaction_id, + o_id, + old_tuple, + key_tuple, + )) } byte => { return Err(ConversionError::Io(io::Error::new( diff --git a/backend/windmill-api/src/database_triggers/trigger.rs b/backend/windmill-api/src/database_triggers/trigger.rs index 65ae534ace32d..84773f2c435d1 100644 --- a/backend/windmill-api/src/database_triggers/trigger.rs +++ b/backend/windmill-api/src/database_triggers/trigger.rs @@ -1,7 +1,9 @@ +use serde_json::value::RawValue; use std::pin::Pin; use crate::{ database_triggers::{ + relation::RelationConverter, replication_message::{ LogicalReplicationMessage::{Begin, Commit, Delete, Insert, Relation, Type, Update}, ReplicationMessage, @@ -465,6 +467,7 @@ async fn listen_to_transactions( Ok(()) } _ = async { + let mut relations = RelationConverter::new(); tracing::info!("Start to listen for database transaction"); loop { let message = logical_replication_stream.next().await; @@ -509,26 +512,33 @@ async fn listen_to_transactions( } }; println!("{:#?}", logical_replication_message); - match logical_replication_message { + let json = match logical_replication_message { Relation(relation_body) => { - //println!("{:#?}", relation_body); + relations.add_column(relation_body.o_id, relation_body.columns); + None } - Begin(begin_body) => { + Begin(_) | Commit(_) | Type(_) => { //println!("{:#?}", begin_body); - } - Commit(commit_body) => { - //println!("{:#?}", commit_body); - }, - Type(type_body) => { - //println!("{:#?}", type_body) + None } Insert(insert) => { - //println!("{:#?}", insert) + Some(relations.body_to_json((insert.o_id, insert.tuple))) + } + Update(update) => { + Some(relations.body_to_json((update.o_id, update.new_tuple))) + } - Update | Delete => { - let _ = run_job(&db, rsmq.clone(), database_trigger).await; + Delete(delete) => { + None } + }; + + if let Some(Ok(json)) = json { + + let _ = run_job(Some(json), &db, rsmq.clone(), database_trigger).await; + continue; } + } } } From 23d942cba415f2aa31fa9a15a4dd236ca31226cc Mon Sep 17 00:00:00 2001 From: dieriba Date: Wed, 11 Dec 2024 01:42:45 +0100 Subject: [PATCH 23/50] feat: :construction: converter done, work on custom script --- .../src/database_triggers/bool.rs | 17 ++ .../src/database_triggers/converter.rs | 272 ++++++++---------- .../src/database_triggers/handler.rs | 4 + .../windmill-api/src/database_triggers/hex.rs | 35 +++ .../windmill-api/src/database_triggers/mod.rs | 7 +- .../src/database_triggers/relation.rs | 8 +- .../database_triggers/replication_message.rs | 16 +- .../src/database_triggers/trigger.rs | 13 +- .../DatabaseTriggerEditorInner.svelte | 10 + 9 files changed, 217 insertions(+), 165 deletions(-) create mode 100644 backend/windmill-api/src/database_triggers/bool.rs create mode 100644 backend/windmill-api/src/database_triggers/hex.rs diff --git a/backend/windmill-api/src/database_triggers/bool.rs b/backend/windmill-api/src/database_triggers/bool.rs new file mode 100644 index 0000000000000..040d8c4a0e744 --- /dev/null +++ b/backend/windmill-api/src/database_triggers/bool.rs @@ -0,0 +1,17 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ParseBoolError { + #[error("invalid input value: {0}")] + InvalidInput(String), +} + +pub fn parse_bool(s: &str) -> Result { + if s == "t" { + Ok(true) + } else if s == "f" { + Ok(false) + } else { + Err(ParseBoolError::InvalidInput(s.to_string())) + } +} diff --git a/backend/windmill-api/src/database_triggers/converter.rs b/backend/windmill-api/src/database_triggers/converter.rs index 787edaa220d59..24315005e9fb0 100644 --- a/backend/windmill-api/src/database_triggers/converter.rs +++ b/backend/windmill-api/src/database_triggers/converter.rs @@ -1,15 +1,19 @@ use core::str; use std::num::{ParseFloatError, ParseIntError}; -use bigdecimal::ParseBigDecimalError; +use super::{ + bool::{parse_bool, ParseBoolError}, + hex::{from_bytea_hex, ByteaHexParseError}, + // numeric::PgNumeric, +}; +//use bigdecimal::ParseBigDecimalError; use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Utc}; +use rust_postgres::types::Type; +use serde::Serialize; +use serde_json::value::RawValue; use thiserror::Error; -use tokio_postgres::types::Type; use uuid::Uuid; - -use crate::conversions::{bool::parse_bool, hex}; - -use super::{bool::ParseBoolError, hex::ByteaHexParseError, numeric::PgNumeric, ArrayCell, Cell}; +use windmill_common::worker::to_raw_value; #[derive(Debug, Error)] pub enum ConverterError { @@ -22,9 +26,8 @@ pub enum ConverterError { #[error("invalid float value")] InvalidFloat(#[from] ParseFloatError), - #[error("invalid numeric: {0}")] - InvalidNumeric(#[from] ParseBigDecimalError), - + /*#[error("invalid numeric: {0}")] + InvalidNumeric(#[from] ParseBigDecimalError),*/ #[error("invalid bytea: {0}")] InvalidBytea(#[from] ByteaHexParseError), @@ -53,155 +56,128 @@ pub enum ArrayParseError { } impl Converter { - pub fn try_from_str(typ: Option<&Type>, str: &str) -> Result { - match *typ { - Type::BOOL => Ok(Cell::Bool(parse_bool(str)?)), - Type::BOOL_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(parse_bool(str)?)), ArrayCell::Bool) - } - Type::CHAR | Type::BPCHAR | Type::VARCHAR | Type::NAME | Type::TEXT => { - Ok(Cell::String(str.to_string())) - } - Type::CHAR_ARRAY - | Type::BPCHAR_ARRAY - | Type::VARCHAR_ARRAY - | Type::NAME_ARRAY - | Type::TEXT_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.to_string())), ArrayCell::String) - } - Type::INT2 => Ok(Cell::I16(str.parse()?)), - Type::INT2_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::I16) - } - Type::INT4 => Ok(Cell::I32(str.parse()?)), - Type::INT4_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::I32) - } - Type::INT8 => Ok(Cell::I64(str.parse()?)), - Type::INT8_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::I64) - } - Type::FLOAT4 => Ok(Cell::F32(str.parse()?)), - Type::FLOAT4_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::F32) - } - Type::FLOAT8 => Ok(Cell::F64(str.parse()?)), - Type::FLOAT8_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::F64) - } - Type::NUMERIC => Ok(Cell::Numeric(str.parse()?)), - Type::NUMERIC_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::Numeric) - } - Type::BYTEA => Ok(Cell::Bytes(hex::from_bytea_hex(str)?)), - Type::BYTEA_ARRAY => Converter::parse_array( - str, - |str| Ok(Some(hex::from_bytea_hex(str)?)), - ArrayCell::Bytes, - ), - Type::DATE => { - let val = NaiveDate::parse_from_str(str, "%Y-%m-%d")?; - Ok(Cell::Date(val)) - } - Type::DATE_ARRAY => Converter::parse_array( - str, - |str| Ok(Some(NaiveDate::parse_from_str(str, "%Y-%m-%d")?)), - ArrayCell::Date, - ), - Type::TIME => { - let val = NaiveTime::parse_from_str(str, "%H:%M:%S%.f")?; - Ok(Cell::Time(val)) - } - Type::TIME_ARRAY => Converter::parse_array( - str, - |str| Ok(Some(NaiveTime::parse_from_str(str, "%H:%M:%S%.f")?)), - ArrayCell::Time, - ), - Type::TIMESTAMP => { - let val = NaiveDateTime::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f")?; - Ok(Cell::TimeStamp(val)) - } - Type::TIMESTAMP_ARRAY => Converter::parse_array( - str, - |str| { - Ok(Some(NaiveDateTime::parse_from_str( - str, - "%Y-%m-%d %H:%M:%S%.f", - )?)) - }, - ArrayCell::TimeStamp, - ), - Type::TIMESTAMPTZ => { - let val = - match DateTime::::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f%#z") { - Ok(val) => val, - Err(_) => { - DateTime::::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f%:z")? - } - }; - Ok(Cell::TimeStampTz(val.into())) - } - Type::TIMESTAMPTZ_ARRAY => { - match Converter::parse_array( - str, - |str| { - Ok(Some( - DateTime::::parse_from_str( + pub fn try_from_str(typ: Option<&Type>, str: &str) -> Result, ConverterError> { + let value = match typ { + Some(typ) => { + match *typ { + Type::BOOL => to_raw_value(&parse_bool(str)?), + Type::BOOL_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(parse_bool(str)?)))? + } + Type::CHAR | Type::BPCHAR | Type::VARCHAR | Type::NAME | Type::TEXT => { + to_raw_value(&str.to_string()) + } + Type::CHAR_ARRAY + | Type::BPCHAR_ARRAY + | Type::VARCHAR_ARRAY + | Type::NAME_ARRAY + | Type::TEXT_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.to_string())))? + } + Type::INT2 => to_raw_value(&str.parse::()?), + Type::INT2_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse::()?)))? + } + Type::INT4 => to_raw_value(&str.parse::()?), + Type::INT4_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse::()?)))? + } + Type::INT8 => to_raw_value(&str.parse::()?), + Type::INT8_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse::()?)))? + } + Type::FLOAT4 => to_raw_value(&str.parse::()?), + Type::FLOAT4_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse::()?)))? + } + Type::FLOAT8 => to_raw_value(&str.parse::()?), + Type::FLOAT8_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse::()?)))? + } + //Type::NUMERIC => Ok(str.parse()?), + //Type::NUMERIC_ARRAY => Converter::parse_array(str, |str| Ok(Some(str.parse()?)))?, + Type::BYTEA => to_raw_value(&from_bytea_hex(str)?), + Type::BYTEA_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(from_bytea_hex(str)?)))? + } + Type::DATE => to_raw_value(&NaiveDate::parse_from_str(str, "%Y-%m-%d")?), + Type::DATE_ARRAY => Converter::parse_array(str, |str| { + Ok(Some(NaiveDate::parse_from_str(str, "%Y-%m-%d")?)) + })?, + Type::TIME => to_raw_value(&NaiveTime::parse_from_str(str, "%H:%M:%S%.f")?), + Type::TIME_ARRAY => Converter::parse_array(str, |str| { + Ok(Some(NaiveTime::parse_from_str(str, "%H:%M:%S%.f")?)) + })?, + Type::TIMESTAMP => { + to_raw_value(&NaiveDateTime::parse_from_str(str, "%Y-%m-%d %H:%M:%S%.f")?) + } + Type::TIMESTAMP_ARRAY => Converter::parse_array(str, |str| { + Ok(Some(NaiveDateTime::parse_from_str( + str, + "%Y-%m-%d %H:%M:%S%.f", + )?)) + })?, + Type::TIMESTAMPTZ => { + let val = match DateTime::::parse_from_str( + str, + "%Y-%m-%d %H:%M:%S%.f%#z", + ) { + Ok(val) => val, + Err(_) => DateTime::::parse_from_str( + str, + "%Y-%m-%d %H:%M:%S%.f%:z", + )?, + }; + let utc: DateTime = val.into(); + to_raw_value(&utc) + } + Type::TIMESTAMPTZ_ARRAY => { + match Converter::parse_array(str, |str| { + let utc: DateTime = DateTime::::parse_from_str( str, "%Y-%m-%d %H:%M:%S%.f%#z", )? - .into(), - )) - }, - ArrayCell::TimeStampTz, - ) { - Ok(val) => Ok(val), - Err(_) => Converter::parse_array( - str, - |str| { - Ok(Some( - DateTime::::parse_from_str( + .into(); + Ok(Some(utc)) + }) { + Ok(val) => val, + Err(_) => Converter::parse_array(str, |str| { + let utc: DateTime = DateTime::::parse_from_str( str, - "%Y-%m-%d %H:%M:%S%.f%:z", + "%Y-%m-%d %H:%M:%S%.f%#z", )? - .into(), - )) - }, - ArrayCell::TimeStampTz, - ), + .into(); + Ok(Some(utc)) + })?, + } + } + Type::UUID => to_raw_value(&Uuid::parse_str(str)?), + Type::UUID_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(Uuid::parse_str(str)?)))? + } + Type::JSON | Type::JSONB => { + to_raw_value(&serde_json::from_str::(str)?) + } + Type::JSON_ARRAY | Type::JSONB_ARRAY => Converter::parse_array(str, |str| { + Ok(Some(serde_json::from_str::(str)?)) + })?, + Type::OID => to_raw_value(&str.parse::()?), + Type::OID_ARRAY => { + Converter::parse_array(str, |str| Ok(Some(str.parse::()?)))? + } + _ => to_raw_value(&str.to_string()), } } - Type::UUID => { - let val = Uuid::parse_str(str)?; - Ok(Cell::Uuid(val)) - } - Type::UUID_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(Uuid::parse_str(str)?)), ArrayCell::Uuid) - } - Type::JSON | Type::JSONB => { - let val = serde_json::from_str(str)?; - Ok(Cell::Json(val)) - } - Type::JSON_ARRAY | Type::JSONB_ARRAY => Converter::parse_array( - str, - |str| Ok(Some(serde_json::from_str(str)?)), - ArrayCell::Json, - ), - Type::OID => { - let val: u32 = str.parse()?; - Ok(Cell::U32(val)) - } - Type::OID_ARRAY => { - Converter::parse_array(str, |str| Ok(Some(str.parse()?)), ArrayCell::U32) - } - _ => Ok(Cell::String(str.to_string())), - } + None => to_raw_value(&str.to_string()), + }; + + Ok(value) } - fn parse_array(str: &str, mut parse: P, m: M) -> Result + fn parse_array(str: &str, mut parse: P) -> Result, ConverterError> where P: FnMut(&str) -> Result, ConverterError>, - M: FnOnce(Vec>) -> ArrayCell, + T: Serialize, { if str.len() < 2 { return Err(ArrayParseError::InputTooShort.into()); @@ -251,6 +227,6 @@ impl Converter { val_str.clear(); } - Ok(Cell::Array(m(res))) + Ok(to_raw_value(&res)) } } diff --git a/backend/windmill-api/src/database_triggers/handler.rs b/backend/windmill-api/src/database_triggers/handler.rs index a0bff2ec93e63..9a6d7b080c921 100644 --- a/backend/windmill-api/src/database_triggers/handler.rs +++ b/backend/windmill-api/src/database_triggers/handler.rs @@ -504,3 +504,7 @@ pub async fn set_enabled( path, payload.enabled )) } + +pub async fn get_custom_script() -> error::Result { + Ok(String::new()) +} \ No newline at end of file diff --git a/backend/windmill-api/src/database_triggers/hex.rs b/backend/windmill-api/src/database_triggers/hex.rs new file mode 100644 index 0000000000000..1538588142fbc --- /dev/null +++ b/backend/windmill-api/src/database_triggers/hex.rs @@ -0,0 +1,35 @@ +use std::num::ParseIntError; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ByteaHexParseError { + #[error("missing prefix '\\x'")] + InvalidPrefix, + + #[error("invalid byte")] + OddNumerOfDigits, + + #[error("parse int result: {0}")] + ParseInt(#[from] ParseIntError), +} + +pub fn from_bytea_hex(s: &str) -> Result, ByteaHexParseError> { + if s.len() < 2 || &s[..2] != "\\x" { + return Err(ByteaHexParseError::InvalidPrefix); + } + + let mut result = Vec::with_capacity((s.len() - 2) / 2); + let s = &s[2..]; + + if s.len() % 2 != 0 { + return Err(ByteaHexParseError::OddNumerOfDigits); + } + + for i in (0..s.len()).step_by(2) { + let val = u8::from_str_radix(&s[i..i + 2], 16)?; + result.push(val); + } + + Ok(result) +} diff --git a/backend/windmill-api/src/database_triggers/mod.rs b/backend/windmill-api/src/database_triggers/mod.rs index a480e9fb1d12b..1e346a42855a5 100644 --- a/backend/windmill-api/src/database_triggers/mod.rs +++ b/backend/windmill-api/src/database_triggers/mod.rs @@ -12,18 +12,20 @@ use axum::{ Router, }; use handler::{ - create_database_trigger, delete_database_trigger, exists_database_trigger, + create_database_trigger, delete_database_trigger, exists_database_trigger, get_custom_script, get_database_trigger, list_database_triggers, set_enabled, update_database_trigger, DatabaseTrigger, }; use windmill_common::{db::UserDB, utils::StripPath}; use windmill_queue::PushArgsOwned; +mod bool; +mod converter; mod handler; +mod hex; mod relation; mod replication_message; mod trigger; -mod converter; pub type SqlxJson = sqlx::types::Json; pub use trigger::start_database; @@ -37,6 +39,7 @@ pub fn workspaced_service() -> Router { .route("/delete/*path", delete(delete_database_trigger)) .route("/exists/*path", get(exists_database_trigger)) .route("/setenabled/*path", post(set_enabled)) + .route("/custom-script", get(get_custom_script)) } async fn run_job( diff --git a/backend/windmill-api/src/database_triggers/relation.rs b/backend/windmill-api/src/database_triggers/relation.rs index dd21ad5dec0c4..003997b4cda1c 100644 --- a/backend/windmill-api/src/database_triggers/relation.rs +++ b/backend/windmill-api/src/database_triggers/relation.rs @@ -60,20 +60,22 @@ impl RelationConverter { let value = match &tuple_data[i] { TupleData::Null => to_raw_value::<&Option<()>>(&&None), TupleData::UnchangedToast => { + println!("{}", "UnchangedToast"); return Err(RelationConversionError::BinaryFormatNotSupported) } - TupleData::Binary(_) => { + TupleData::Binary(s) => { return Err(RelationConversionError::BinaryFormatNotSupported) } TupleData::Text(bytes) => { - let str = str::from_utf8(&bytes)?; + let str = str::from_utf8(&bytes[..])?; + println!("{:#?}", &bytes); Converter::try_from_str(column.type_o_id.as_ref(), str)? } }; object.insert(column.name.clone(), value); } - + println!("{:#?}", &object); Ok(object) } } diff --git a/backend/windmill-api/src/database_triggers/replication_message.rs b/backend/windmill-api/src/database_triggers/replication_message.rs index 5a7b753b398b6..f518a5505e65c 100644 --- a/backend/windmill-api/src/database_triggers/replication_message.rs +++ b/backend/windmill-api/src/database_triggers/replication_message.rs @@ -184,8 +184,8 @@ impl UpdateBody { pub struct DeleteBody { transaction_id: Option, pub o_id: Oid, - old_tuple: Option>, - key_tuple: Option>, + pub old_tuple: Option>, + pub key_tuple: Option>, } impl DeleteBody { @@ -211,6 +211,7 @@ impl TupleData { fn parse(buf: &mut Buffer) -> Result, ConversionError> { let number_of_columns = buf.read_i16::()?; let mut tuples = Vec::with_capacity(number_of_columns as usize); + println!("Number of column: {}", number_of_columns); for _ in 0..number_of_columns { let byte = buf.read_u8()?; let tuple_data = match byte { @@ -218,13 +219,16 @@ impl TupleData { TUPLE_DATA_TOAST_BYTE => TupleData::UnchangedToast, TUPLE_DATA_TEXT_BYTE => { let len = buf.read_i32::()?; - let bytes = buf.read_n_byte(len as usize); - TupleData::Text(bytes) + let mut data = vec![0; len as usize]; + buf.read_exact(&mut data)?; + TupleData::Text(data.into()) } TUPLE_DATA_BINARY_BYTE => { let len = buf.read_i32::()?; - let bytes = buf.read_n_byte(len as usize); - TupleData::Text(bytes) + let mut data = vec![0; len as usize]; + println!("Binary: {:#?}", &buf.bytes[buf.idx..buf.idx + len as usize]); + buf.read_exact(&mut data)?; + TupleData::Binary(data.into()) } byte => { return Err(ConversionError::Io(io::Error::new( diff --git a/backend/windmill-api/src/database_triggers/trigger.rs b/backend/windmill-api/src/database_triggers/trigger.rs index 84773f2c435d1..0f706abf26fb6 100644 --- a/backend/windmill-api/src/database_triggers/trigger.rs +++ b/backend/windmill-api/src/database_triggers/trigger.rs @@ -165,7 +165,8 @@ impl PostgresClient { let binary_format = true; let options = match binary_format { true => format!( - r#"("proto_version" '2', "publication_names" {}, "binary")"#, + r#"("proto_version" '2', "publication_names" {})"#, + //r#"("proto_version" '2', "publication_names" {}, "binary")"#, quote_literal(publication_name), ), false => format!( @@ -511,30 +512,30 @@ async fn listen_to_transactions( return; } }; - println!("{:#?}", logical_replication_message); + let json = match logical_replication_message { Relation(relation_body) => { relations.add_column(relation_body.o_id, relation_body.columns); None } Begin(_) | Commit(_) | Type(_) => { - //println!("{:#?}", begin_body); None } Insert(insert) => { Some(relations.body_to_json((insert.o_id, insert.tuple))) } Update(update) => { - Some(relations.body_to_json((update.o_id, update.new_tuple))) + let _ = update.old_tuple.unwrap_or(update.key_tuple.unwrap()); + Some(relations.body_to_json((update.o_id, update.new_tuple))) } Delete(delete) => { - None + let body = delete.old_tuple.unwrap_or(delete.key_tuple.unwrap()); + Some(relations.body_to_json((delete.o_id, body))) } }; if let Some(Ok(json)) = json { - let _ = run_job(Some(json), &db, rsmq.clone(), database_trigger).await; continue; } diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index abfb0bc83716a..b602355008751 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -222,6 +222,16 @@ bind:scriptPath={script_path} allowRefresh /> + + {#if script_path === undefined} + + {/if}
From da4264e4f1f41604e0ba91df00ac3e1abe0a4e22 Mon Sep 17 00:00:00 2001 From: dieriba Date: Thu, 12 Dec 2024 05:26:49 +0100 Subject: [PATCH 24/50] feat: :construction: multiple trigger --- .../20241123152203_database_triggers.up.sql | 1 + backend/windmill-api/openapi.yaml | 40 ++- .../src/database_triggers/handler.rs | 83 +++++- .../src/database_triggers/relation.rs | 10 +- .../database_triggers/replication_message.rs | 3 +- .../src/database_triggers/trigger.rs | 241 +++++++++++++++--- .../DatabaseTriggerEditorInner.svelte | 105 +++++--- 7 files changed, 382 insertions(+), 101 deletions(-) diff --git a/backend/migrations/20241123152203_database_triggers.up.sql b/backend/migrations/20241123152203_database_triggers.up.sql index c6773ba424dd7..74f506663e745 100644 --- a/backend/migrations/20241123152203_database_triggers.up.sql +++ b/backend/migrations/20241123152203_database_triggers.up.sql @@ -17,6 +17,7 @@ CREATE TABLE database_trigger( last_server_ping TIMESTAMPTZ NULL, replication_slot_name VARCHAR(255) NOT NULL, publication_name VARCHAR(255) NOT NULL, + last_lsn bigint NOT NULL DEFAULT 0, enabled BOOLEAN NOT NULL, CONSTRAINT PK_database_trigger PRIMARY KEY (path,workspace_id) ); \ No newline at end of file diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index b6e1c0781d412..e2a1f4ab1ee1c 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -12249,17 +12249,30 @@ components: - runnable_result TableToTrack: + type: array + items: + type: object + properties: + table_name: + type: string + columns_name: + type: array + items: + type: string + required: + - table_name + - columns_name + + Relations: type: object properties: - table_name: + schema_name: type: string - columns_name: - type: array - items: - type: string + table_to_track: + $ref: "#/components/schemas/TableToTrack" required: - - table_name - - columns_name + - schema_name + - table_to_track DatabaseTrigger: allOf: @@ -12279,7 +12292,7 @@ components: table_to_track: type: array items: - $ref: "#/components/schemas/TableToTrack" + $ref: "#/components/schemas/Relations" transaction_to_track: type: array items: @@ -12326,7 +12339,7 @@ components: table_to_track: type: array items: - $ref: "#/components/schemas/TableToTrack" + $ref: "#/components/schemas/Relations" transaction_to_track: type: array items: @@ -12340,6 +12353,8 @@ components: - transaction_to_track - replication_slot_name - publication_name + - table_to_track + EditDatabaseTrigger: type: object properties: @@ -12356,13 +12371,18 @@ components: table_to_track: type: array items: - $ref: "#/components/schemas/TableToTrack" + $ref: "#/components/schemas/Relations" + transaction_to_track: + type: array + items: + $ref: "#/components/schemas/TransactionType" required: - path - script_path - is_flow - enabled - database_resource_path + - table_to_track - transaction_to_track Group: diff --git a/backend/windmill-api/src/database_triggers/handler.rs b/backend/windmill-api/src/database_triggers/handler.rs index 9a6d7b080c921..9649c76cbdff0 100644 --- a/backend/windmill-api/src/database_triggers/handler.rs +++ b/backend/windmill-api/src/database_triggers/handler.rs @@ -1,8 +1,11 @@ +use std::collections::HashSet; + use axum::{ extract::{Path, Query}, Extension, Json, }; use http::StatusCode; +use itertools::Itertools; use serde::{Deserialize, Deserializer, Serialize}; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::FromRow; @@ -41,13 +44,22 @@ pub struct TableToTrack { pub columns_name: Vec, } +#[derive(FromRow, Serialize, Deserialize, Debug)] +pub struct Relations { + pub schema_name: String, + pub table_to_track: Vec, +} + #[derive(Deserialize)] pub struct EditDatabaseTrigger { path: String, script_path: String, is_flow: bool, database_resource_path: String, - table_to_track: Option>, + #[serde(deserialize_with = "check_if_not_duplication_relation")] + table_to_track: Option>, + #[serde(deserialize_with = "check_if_valid_transaction_type")] + transaction_to_track: Vec, } #[derive(Deserialize, Serialize, Debug)] @@ -60,11 +72,53 @@ pub struct NewDatabaseTrigger { is_flow: bool, enabled: bool, database_resource_path: String, - table_to_track: Option>, + #[serde(deserialize_with = "check_if_not_duplication_relation")] + table_to_track: Option>, replication_slot_name: String, publication_name: String, } +fn check_if_not_duplication_relation<'de, D>( + relations: D, +) -> std::result::Result>, D::Error> +where + D: Deserializer<'de>, +{ + let relations: Option> = Option::deserialize(relations)?; + + match relations { + Some(relations) => { + let relations = relations + .into_iter() + .filter_map(|relation| { + if relation.schema_name.is_empty() + && !relation + .table_to_track + .iter() + .any(|table| !table.table_name.is_empty()) + { + return None; + } + Some(relation) + }) + .collect_vec(); + + if !relations + .iter() + .map(|relation| relation.schema_name.as_str()) + .all_unique() + { + return Err(serde::de::Error::custom( + "You cannot choose a schema more than one time".to_string(), + )); + } + + Ok(Some(relations)) + } + None => Ok(None), + } +} + fn check_if_valid_transaction_type<'de, D>( transaction_type: D, ) -> std::result::Result, D::Error> @@ -112,7 +166,7 @@ pub struct DatabaseTrigger { pub extra_perms: Option, pub database_resource_path: String, pub transaction_to_track: Option>, - pub table_to_track: Option>>, + pub table_to_track: Option>>, pub error: Option, pub server_id: Option, pub replication_slot_name: String, @@ -313,7 +367,7 @@ pub async fn get_database_trigger( replication_slot_name, publication_name, database_resource_path, - table_to_track AS "table_to_track: SqlxJson>" + table_to_track AS "table_to_track: SqlxJson>" FROM database_trigger WHERE @@ -339,8 +393,14 @@ pub async fn update_database_trigger( Json(database_trigger): Json, ) -> error::Result { let workspace_path = path.to_path(); - let EditDatabaseTrigger { script_path, path, is_flow, database_resource_path, table_to_track } = - database_trigger; + let EditDatabaseTrigger { + script_path, + path, + is_flow, + database_resource_path, + table_to_track, + transaction_to_track, + } = database_trigger; let mut tx = user_db.begin(&authed).await?; let table_to_track = serde_json::to_value(table_to_track).unwrap(); @@ -356,11 +416,13 @@ pub async fn update_database_trigger( email = $5, database_resource_path = $6, table_to_track = $7, + transaction_to_track = $8, edited_at = now(), - error = NULL + error = NULL, + server_id = NULL WHERE - workspace_id = $8 AND - path = $9 + workspace_id = $9 AND + path = $10 "#, script_path, path, @@ -369,6 +431,7 @@ pub async fn update_database_trigger( &authed.email, database_resource_path, table_to_track, + transaction_to_track as Vec, w_id, workspace_path, ) @@ -507,4 +570,4 @@ pub async fn set_enabled( pub async fn get_custom_script() -> error::Result { Ok(String::new()) -} \ No newline at end of file +} diff --git a/backend/windmill-api/src/database_triggers/relation.rs b/backend/windmill-api/src/database_triggers/relation.rs index 003997b4cda1c..4550fd90dd8e2 100644 --- a/backend/windmill-api/src/database_triggers/relation.rs +++ b/backend/windmill-api/src/database_triggers/relation.rs @@ -12,9 +12,6 @@ use serde_json::Value; use windmill_common::worker::to_raw_value; #[derive(Debug, thiserror::Error)] pub enum RelationConversionError { - #[error("Json error: {0}")] - FailConversionToJson(serde_json::Error), - #[error("Could not find matching table")] FailToFindMatchingTable, @@ -58,17 +55,12 @@ impl RelationConverter { for (i, column) in columns.iter().enumerate() { let value = match &tuple_data[i] { - TupleData::Null => to_raw_value::<&Option<()>>(&&None), - TupleData::UnchangedToast => { - println!("{}", "UnchangedToast"); - return Err(RelationConversionError::BinaryFormatNotSupported) - } + TupleData::Null | TupleData::UnchangedToast => to_raw_value::<&Option<()>>(&&None), TupleData::Binary(s) => { return Err(RelationConversionError::BinaryFormatNotSupported) } TupleData::Text(bytes) => { let str = str::from_utf8(&bytes[..])?; - println!("{:#?}", &bytes); Converter::try_from_str(column.type_o_id.as_ref(), str)? } }; diff --git a/backend/windmill-api/src/database_triggers/replication_message.rs b/backend/windmill-api/src/database_triggers/replication_message.rs index f518a5505e65c..eee9c708a7025 100644 --- a/backend/windmill-api/src/database_triggers/replication_message.rs +++ b/backend/windmill-api/src/database_triggers/replication_message.rs @@ -211,7 +211,6 @@ impl TupleData { fn parse(buf: &mut Buffer) -> Result, ConversionError> { let number_of_columns = buf.read_i16::()?; let mut tuples = Vec::with_capacity(number_of_columns as usize); - println!("Number of column: {}", number_of_columns); for _ in 0..number_of_columns { let byte = buf.read_u8()?; let tuple_data = match byte { @@ -437,13 +436,13 @@ impl XLogDataBody { LogicalReplicationMessage::Insert(InsertBody::new(transaction_id, o_id, tuple)) } UPDATE_BYTE => { + let transaction_id = match logical_replication_settings.streaming { true => Some(buf.read_i32::()?), false => None, }; let o_id = buf.read_u32::()?; let byte = buf.read_u8()?; - let mut key_tuple = None; let mut old_tuple = None; diff --git a/backend/windmill-api/src/database_triggers/trigger.rs b/backend/windmill-api/src/database_triggers/trigger.rs index 0f706abf26fb6..e31b61fb37f95 100644 --- a/backend/windmill-api/src/database_triggers/trigger.rs +++ b/backend/windmill-api/src/database_triggers/trigger.rs @@ -19,10 +19,12 @@ use itertools::Itertools; use pg_escape::{quote_identifier, quote_literal}; use rand::seq::SliceRandom; use rust_postgres::{Client, Config, CopyBothDuplex, NoTls, SimpleQueryMessage}; -use windmill_common::{resource::get_resource, variables::get_variable_or_self, INSTANCE_NAME}; +use windmill_common::{ + resource::get_resource, variables::get_variable_or_self, worker::to_raw_value, INSTANCE_NAME, +}; use super::{ - handler::{Database, DatabaseTrigger, TableToTrack, TransactionType}, + handler::{Database, DatabaseTrigger, Relations, TransactionType}, replication_message::PrimaryKeepAliveBody, SqlxJson, }; @@ -103,16 +105,15 @@ impl PostgresClient { pub async fn check_if_table_exists( &self, - db_name: &str, table_to_track: &[&str], + catalog: &str, + db_name: &str, ) -> Result<(), Error> { - if table_to_track.is_empty() { - return Ok(()); - } let table_names = table_to_track .iter() .map(|table| quote_literal(table)) .join(","); + let query = format!( r#" WITH target_tables AS ( @@ -128,19 +129,22 @@ impl PostgresClient { AND ist.table_type = 'BASE TABLE' AND ist.table_catalog = {} AND ist.table_schema NOT IN ('pg_catalog', 'information_schema') + AND ist.table_schema = {} WHERE ist.table_name IS NULL; "#, table_names, - quote_literal(db_name) + quote_literal(db_name), + quote_literal(catalog) ); + let rows = self .client .simple_query(&query) .await .map_err(Error::Postgres)?; - if !rows.row_exist() { + if !rows.row_exist() { return Ok(()); } @@ -252,6 +256,88 @@ impl PostgresClient { self.create_slot(slot_name).await } + async fn atler_publication( + &self, + publication_name: &str, + relations: Option<&[Relations]>, + transaction_to_track: Option<&[TransactionType]>, + ) -> Result<(), Error> { + let mut query = String::new(); + let quoted_publication_name = quote_identifier(publication_name); + query.push_str("ALTER PUBLICATION "); + query.push_str("ed_publication_name); + + match relations { + Some(relations) if !relations.is_empty() => { + query.push_str(" SET"); + for (i, relation) in relations.iter().enumerate() { + if !relation + .table_to_track + .iter() + .any(|table| !table.table_name.is_empty()) + { + query.push_str(" TABLES IN SCHEMA "); + let quoted_schema = quote_identifier(&relation.schema_name); + query.push_str("ed_schema); + } else { + query.push_str(" TABLE ONLY "); + for (j, table) in relation + .table_to_track + .iter() + .filter(|table| !table.table_name.is_empty()) + .enumerate() + { + let quoted_table = quote_identifier(&table.table_name); + query.push_str("ed_table); + if !table.columns_name.is_empty() { + query.push_str(" ("); + let columns = table.columns_name.iter().join(","); + query.push_str(&columns); + query.push(')'); + } + if j + 1 != relation.table_to_track.len() { + query.push_str(", ") + } + } + } + if i < relations.len() - 1 { + query.push(',') + } + } + } + _ => query.push_str(" FOR ALL TABLES "), + }; + + if let Some(transaction_to_track) = transaction_to_track { + query.push_str("; ALTER PUBLICATION "); + query.push_str("ed_publication_name); + if !transaction_to_track.is_empty() { + let transactions = || { + transaction_to_track + .iter() + .map(|transaction| match transaction { + TransactionType::Insert => "insert", + TransactionType::Update => "update", + TransactionType::Delete => "delete", + }) + .join(",") + }; + let with_parameter = format!(" SET (publish = '{}'); ", transactions()); + query.push_str(&with_parameter); + } else { + query.push_str(" SET (publish = 'insert,update,delete')"); + } + } + + + self.client + .simple_query(&query) + .await + .map_err(Error::Postgres)?; + + Ok(()) + } + async fn check_if_publication_exists(&self, publication: &str) -> Result { let publication_exists_query = format!( "select 1 as exists from pg_publication where pubname = {};", @@ -273,7 +359,7 @@ impl PostgresClient { async fn create_publication( &self, publication_name: &str, - tables: Option>, + relations: Option<&[Relations]>, transaction_to_track: Option<&[TransactionType]>, ) -> Result<(), Error> { let mut query = String::new(); @@ -281,14 +367,40 @@ impl PostgresClient { query.push_str("CREATE PUBLICATION "); query.push_str("ed_publication_name); - match tables { - Some(table_to_track) if !table_to_track.is_empty() => { - query.push_str(" FOR TABLE ONLY "); - for (i, table) in table_to_track.iter().enumerate() { - let quoted_table = quote_identifier(table); - query.push_str("ed_table); - - if i < table_to_track.len() - 1 { + match relations { + Some(relations) if !relations.is_empty() => { + query.push_str(" FOR "); + for (i, relation) in relations.iter().enumerate() { + if !relation + .table_to_track + .iter() + .any(|table| !table.table_name.is_empty()) + { + query.push_str(" TABLES IN SCHEMA "); + let quoted_schema = quote_identifier(&relation.schema_name); + query.push_str("ed_schema); + } else { + query.push_str(" TABLE ONLY "); + for (j, table) in relation + .table_to_track + .iter() + .filter(|table| !table.table_name.is_empty()) + .enumerate() + { + let quoted_table = quote_identifier(&table.table_name); + query.push_str("ed_table); + if !table.columns_name.is_empty() { + query.push_str(" ("); + let columns = table.columns_name.iter().join(","); + query.push_str(&columns); + query.push(')'); + } + if j + 1 != relation.table_to_track.len() { + query.push_str(", ") + } + } + } + if i < relations.len() - 1 { query.push(',') } } @@ -326,11 +438,13 @@ impl PostgresClient { pub async fn create_publication_if_not_exist( &self, publication_name: &str, - tables: Option>, + tables: Option<&[Relations]>, transaction_to_track: Option<&[TransactionType]>, ) -> Result<(), Error> { if self.check_if_publication_exists(publication_name).await? { tracing::info!("Publication {} already exists", publication_name); + self.atler_publication(publication_name, tables, transaction_to_track) + .await?; return Ok(()); } tracing::info!("Publication {} do no exist", publication_name); @@ -381,6 +495,7 @@ async fn update_ping( database_trigger.path, err ); + return None; } }; @@ -424,13 +539,29 @@ async fn listen_to_transactions( let client = PostgresClient::new(&resource.value).await?; let table_to_track = if let Some(table_to_track) = &database_trigger.table_to_track { - let table_to_track = table_to_track - .iter() - .map(|table| table.table_name.as_str()) - .collect_vec(); - client - .check_if_table_exists(&resource.value.db_name, table_to_track.as_slice()) - .await?; + for relation in table_to_track.0.iter() { + let tables = relation + .table_to_track + .iter() + .filter_map(|table_to_track| { + if table_to_track.table_name.is_empty() { + None + } else { + Some(table_to_track.table_name.as_str()) + } + }) + .collect_vec(); + if tables.is_empty() { + continue; + } + client + .check_if_table_exists( + tables.as_slice(), + &relation.schema_name, + &resource.value.db_name, + ) + .await?; + } Some(table_to_track) } else { None @@ -445,7 +576,7 @@ async fn listen_to_transactions( client .create_publication_if_not_exist( &database_trigger.publication_name, - table_to_track, + table_to_track.map(|v| &***v), database_trigger.transaction_to_track.as_deref(), ) .await?; @@ -497,6 +628,7 @@ async fn listen_to_transactions( } }; + match logical_message { ReplicationMessage::PrimaryKeepAlive(primary_keep_alive) => { if primary_keep_alive.reply { @@ -507,7 +639,6 @@ async fn listen_to_transactions( let logical_replication_message = match x_log_data.parse(&logicail_replication_settings) { Ok(logical_replication_message) => logical_replication_message, Err(err) => { - tracing::debug!("{}", err.to_string()); update_ping(&db, database_trigger, Some(&err.to_string())).await; return; } @@ -518,25 +649,61 @@ async fn listen_to_transactions( relations.add_column(relation_body.o_id, relation_body.columns); None } - Begin(_) | Commit(_) | Type(_) => { + Begin(_) | Type(_) => { + None + } + Commit(commit) => { + match sqlx::query_scalar!( + r#" + UPDATE + database_trigger + SET + last_lsn = $1 + WHERE + workspace_id = $2 + AND path = $3 + AND server_id = $4 + AND enabled IS TRUE + RETURNING 1 + "#, + commit.end_lsn as i64, + &database_trigger.workspace_id, + &database_trigger.path, + *INSTANCE_NAME + ) + .fetch_optional(&db) + .await + { + Ok(updated) => { + if updated.flatten().is_none() { + return; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of database {}: {:?}", + database_trigger.path, + err + ); + return; + } + }; None } Insert(insert) => { - Some(relations.body_to_json((insert.o_id, insert.tuple))) + Some((relations.body_to_json((insert.o_id, insert.tuple)), "insert")) } Update(update) => { - let _ = update.old_tuple.unwrap_or(update.key_tuple.unwrap()); - - Some(relations.body_to_json((update.o_id, update.new_tuple))) + Some((relations.body_to_json((update.o_id, update.new_tuple)), "update")) } Delete(delete) => { let body = delete.old_tuple.unwrap_or(delete.key_tuple.unwrap()); - Some(relations.body_to_json((delete.o_id, body))) + Some((relations.body_to_json((delete.o_id, body)), "delete")) } }; - - if let Some(Ok(json)) = json { - let _ = run_job(Some(json), &db, rsmq.clone(), database_trigger).await; + if let Some((Ok(mut body), transaction_type)) = json { + body.insert("transaction_type".to_string(), to_raw_value(&transaction_type)); + let _ = run_job(Some(body), &db, rsmq.clone(), database_trigger).await; continue; } @@ -628,7 +795,7 @@ async fn listen_to_unlistened_database_events( error, enabled, database_resource_path, - table_to_track AS "table_to_track: SqlxJson>" + table_to_track AS "table_to_track: SqlxJson>" FROM database_trigger WHERE diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index b602355008751..e31dc16ad1c25 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -5,7 +5,7 @@ import Path from '$lib/components/Path.svelte' import Required from '$lib/components/Required.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import { DatabaseTriggerService, type TableToTrack, type TransactionType } from '$lib/gen' + import { DatabaseTriggerService, type Relations, type TransactionType } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, sendUserToast } from '$lib/utils' import { createEventDispatcher } from 'svelte' @@ -34,9 +34,9 @@ let database_resource_path = '' let publication_name: string = '' let replication_slot_name: string = '' - let tableToTrack: TableToTrack[] = [] + let relations: Relations[] = [] let transactionType: TransactionType[] = ['Insert', 'Update', 'Delete'] - let transactionValue: TransactionType[] = [] + let transactionToTrack: TransactionType[] = [] const dispatch = createEventDispatcher() $: is_flow = itemKind === 'flow' @@ -69,7 +69,7 @@ script_path = fixedScriptPath path = '' initialPath = '' - tableToTrack = [] + relations = [] dirtyPath = false replication_slot_name = '' publication_name = '' @@ -90,8 +90,8 @@ path = s.path enabled = s.enabled database_resource_path = s.database_resource_path - tableToTrack = s.table_to_track as TableToTrack[] - transactionValue = s.transaction_to_track + relations = s.table_to_track as Relations[] + transactionToTrack = s.transaction_to_track publication_name = s.publication_name replication_slot_name = s.replication_slot_name can_write = canWrite(s.path, s.extra_perms, $userStore) @@ -108,7 +108,8 @@ is_flow, database_resource_path, enabled, - table_to_track: tableToTrack + table_to_track: relations, + transaction_to_track: transactionToTrack, } }) sendUserToast(`Route ${path} updated`) @@ -116,7 +117,7 @@ await DatabaseTriggerService.createDatabaseTrigger({ workspace: $workspaceStore!, requestBody: { - transaction_to_track: transactionValue, + transaction_to_track: transactionToTrack, path, script_path, is_flow, @@ -124,7 +125,7 @@ database_resource_path, replication_slot_name, publication_name, - table_to_track: tableToTrack + table_to_track: relations } }) sendUserToast(`Route ${path} created`) @@ -271,44 +272,82 @@
-
+

- Tables will limit the execution of the trigger to only the specified tables.
+ Relations will limit the execution of the trigger to only the specified tables.
If no tables are selected, this will trigger for all tables.

- {#each tableToTrack as v, i} + {#each relations as v, i}
- - + {#each v.table_to_track as table_to_track, j} +
+
+ + + +
+ +
+ {/each} +
-
-

- Choose a publication name -

- -
- -
-

- Choose a slot name -

- +
+
+
+

+ Choose a publication name +

+ +
+
+

+ Choose a slot name +

+ +
+
@@ -292,8 +297,8 @@ {#each v.table_to_track as table_to_track, j} -
-
+
+
-
{/each} {/if} From e41965a2fe116b30e63affce30a1d9601e3081ce Mon Sep 17 00:00:00 2001 From: dieriba Date: Tue, 17 Dec 2024 00:58:25 +0100 Subject: [PATCH 28/50] feat: :construction: template script fix bug, work on restructing backend logic --- .../src/database_triggers/handler.rs | 14 +-- .../src/database_triggers/trigger.rs | 7 +- .../src/lib/components/ScriptBuilder.svelte | 36 +++++++- .../DatabaseTriggerEditorInner.svelte | 87 +++++++++++-------- .../DatabaseTriggersPanel.svelte | 9 +- frontend/src/lib/script_helpers.ts | 43 +++++++-- frontend/src/lib/stores.ts | 3 +- 7 files changed, 134 insertions(+), 65 deletions(-) diff --git a/backend/windmill-api/src/database_triggers/handler.rs b/backend/windmill-api/src/database_triggers/handler.rs index 2de364180a7b0..608a3328f73e3 100644 --- a/backend/windmill-api/src/database_triggers/handler.rs +++ b/backend/windmill-api/src/database_triggers/handler.rs @@ -679,13 +679,13 @@ pub async fn get_template_script( let fully_qualified_name = format!("{}.{}", &relation.schema_name, table_to_track.table_name); schema_or_fully_qualified_name.push(quote_literal(&fully_qualified_name)); - columns_list.push( - table_to_track - .columns_name - .into_iter() - .map(|column_name| quote_literal(&column_name)) - .join(","), - ); + + let columns = if !table_to_track.columns_name.is_empty() { + quote_literal(&table_to_track.columns_name.join(",")) + } else { + "''".to_string() + }; + columns_list.push(columns); } continue; } diff --git a/backend/windmill-api/src/database_triggers/trigger.rs b/backend/windmill-api/src/database_triggers/trigger.rs index 9dd82c3d32064..dac98e012ec7a 100644 --- a/backend/windmill-api/src/database_triggers/trigger.rs +++ b/backend/windmill-api/src/database_triggers/trigger.rs @@ -297,7 +297,7 @@ impl PostgresClient { if let Some(where_clause) = &table.where_clause { //query.push_str("WHERE "); } - + if j + 1 != relation.table_to_track.len() { query.push_str(", ") } @@ -309,7 +309,8 @@ impl PostgresClient { } } _ => { - let to_execute = format!(r#" + let to_execute = format!( + r#" DROP PUBLICATION {}; CREATE @@ -724,7 +725,7 @@ async fn listen_to_transactions( let database_info = HashMap::from([("schema_name".to_string(), relation.namespace.as_str()), ("table_name".to_string(), relation.name.as_str()), ("transaction_type".to_string(), transaction_type)]); let extra = Some(HashMap::from([( "wm_trigger".to_string(), - to_raw_value(&serde_json::json!({"kind": "database", "trigger_info": database_info })), + to_raw_value(&serde_json::json!({"kind": "database", "database": serde_json::json!({ "trigger_info": database_info }) })), )])); body.insert("trigger_info".to_string(), to_raw_value(&database_info)); let body = HashMap::from([("data".to_string(), to_raw_value(&serde_json::json!(body)))]); diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 3635adc3d1694..becb5f71d7647 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -10,7 +10,13 @@ } from '$lib/gen' import { inferArgs } from '$lib/infer' import { initialCode } from '$lib/script_helpers' - import { defaultScripts, enterpriseLicense, userStore, workspaceStore } from '$lib/stores' + import { + defaultScripts, + enterpriseLicense, + templateScript, + userStore, + workspaceStore + } from '$lib/stores' import { cleanValueProperties, emptySchema, @@ -109,7 +115,14 @@ ) const simplifiedPoll = writable(false) const selectedTriggerStore = writable< - 'webhooks' | 'emails' | 'schedules' | 'cli' | 'routes' | 'websockets' | 'scheduledPoll' | 'database' + | 'webhooks' + | 'emails' + | 'schedules' + | 'cli' + | 'routes' + | 'websockets' + | 'scheduledPoll' + | 'database' >('webhooks') export function setPrimarySchedule(schedule: ScheduleTrigger | undefined | false) { @@ -216,10 +229,25 @@ function initContent( language: SupportedLanguage, kind: Script['kind'] | undefined, - template: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' | 'bunnative' + template: + | 'pgsql' + | 'mysql' + | 'flow' + | 'script' + | 'fetch' + | 'docker' + | 'powershell' + | 'bunnative' + | 'preprocessor' + | undefined ) { scriptEditor?.disableCollaboration() - script.content = initialCode(language, kind, template) + let getInitBlockTemplate = $templateScript != undefined + script.content = initialCode(language, kind, template, getInitBlockTemplate) + if (getInitBlockTemplate) { + script.content += '\r\n' + $templateScript + templateScript.set(undefined) + } scriptEditor?.inferSchema(script.content, language) if (script.content != editor?.getCode()) { setCode(script.content) diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index cef2afd9f3cf3..3f5b582bb1ea9 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -11,7 +11,7 @@ type Relations, type TransactionType } from '$lib/gen' - import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' + import { templateScript, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, sendUserToast } from '$lib/utils' import { createEventDispatcher } from 'svelte' import Section from '$lib/components/Section.svelte' @@ -21,6 +21,8 @@ import ResourcePicker from '$lib/components/ResourcePicker.svelte' import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte' import { fade } from 'svelte/transition' + import { goto } from '$app/navigation' + import { base } from '$app/paths' let drawer: Drawer let is_flow: boolean = false @@ -44,6 +46,7 @@ let transactionToTrack: TransactionType[] = [] let languages = 'bun,deno' let language: Language = 'Typescript' + let loading = false const dispatch = createEventDispatcher() $: is_flow = itemKind === 'flow' @@ -157,15 +160,22 @@ return } - let template = await DatabaseTriggerService.getTemplateScript({ - workspace: $workspaceStore!, - requestBody: { - relations, - language, - database_resource_path - } - }) - console.log({ template }) + try { + loading = true + let template = await DatabaseTriggerService.getTemplateScript({ + workspace: $workspaceStore!, + requestBody: { + relations, + language, + database_resource_path + } + }) + templateScript.set(template) + await goto(`${base}/scripts/add`) + } catch (error) { + loading = false + console.log({ error }) + } } @@ -239,6 +249,30 @@
+
+

+ Pick a database to connect to +

+
+ +
+
+ +
+

+ Choose what kind of database transaction you want to track allowed operations are + Insert, Update, Delete +

+ + +
+

Pick a script or flow to be triggered @@ -261,19 +295,13 @@ color="dark" size="xs" on:click={getTemplateScript} - target="_blank">Create from template + target="_blank" + {loading} + >Create from template + {/if}

-
-

- Pick a database to connect to -

-
- -
-
@@ -296,21 +324,6 @@
-
-

- Choose what kind of database transaction you want to track allowed operations are - Insert, Update, Delete -

- - -
-

Relations will limit the execution of the trigger to only the specified tables.
@@ -380,7 +393,7 @@ }} startIcon={{ icon: Plus }} > - Add item + Add Table

import { userStore, workspaceStore } from '$lib/stores' - import { WebsocketTriggerService, type WebsocketTrigger } from '$lib/gen' + import { DatabaseTriggerService, type DatabaseTrigger } from '$lib/gen' import { UnplugIcon } from 'lucide-svelte' import { canWrite } from '$lib/utils' @@ -20,11 +20,11 @@ const { triggersCount } = getContext('TriggerContext') - let databaseTriggers: (WebsocketTrigger & { canWrite: boolean })[] | undefined = undefined + let databaseTriggers: (DatabaseTrigger & { canWrite: boolean })[] | undefined = undefined export async function loadTriggers() { try { databaseTriggers = ( - await WebsocketTriggerService.listWebsocketTriggers({ + await DatabaseTriggerService.listDatabaseTriggers({ workspace: $workspaceStore ?? '', path, isFlow @@ -75,9 +75,6 @@ {#each databaseTriggers as databaseTriggers (databaseTriggers.path)}
{databaseTriggers.path}
-
- {databaseTriggers.url} -
@@ -289,7 +317,7 @@ allowRefresh /> - {#if script_path === undefined} + {#if script_path === undefined && is_flow === false} + {/if} From 58130636c593b39f3473a1f5dd8ca13c7799fab1 Mon Sep 17 00:00:00 2001 From: dieriba Date: Fri, 20 Dec 2024 04:08:03 +0100 Subject: [PATCH 31/50] feat: :construction: rewrited crud function --- .../src/database_triggers/client.rs | 205 ------ .../src/database_triggers/handler.rs | 677 ++++++++++-------- .../windmill-api/src/database_triggers/mod.rs | 2 - .../src/database_triggers/trigger.rs | 154 +++- .../DatabaseTriggerEditorInner.svelte | 147 ++-- 5 files changed, 591 insertions(+), 594 deletions(-) delete mode 100644 backend/windmill-api/src/database_triggers/client.rs diff --git a/backend/windmill-api/src/database_triggers/client.rs b/backend/windmill-api/src/database_triggers/client.rs deleted file mode 100644 index 8b898b942ec8c..0000000000000 --- a/backend/windmill-api/src/database_triggers/client.rs +++ /dev/null @@ -1,205 +0,0 @@ -use std::{ - ops::{Deref, DerefMut}, - pin::Pin, -}; - -use bytes::{BufMut, Bytes, BytesMut}; -use chrono::TimeZone; -use futures::SinkExt; -use pg_escape::{quote_identifier, quote_literal}; -use rust_postgres::{Client, Config, CopyBothDuplex, NoTls, SimpleQueryMessage}; -use sqlx::{postgres::PgConnectOptions, Connection, PgConnection}; - -use super::{ - handler::Database, replication_message::PrimaryKeepAliveBody, - trigger::LogicalReplicationSettings, -}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Error from database: {0}")] - Sqlx(sqlx::Error), - #[error("The following tables do not exist in your database: {0}")] - MissingTables(String), - #[error("Error from database: {0}")] - Postgres(rust_postgres::Error), - #[error("{0}")] - CommonError(windmill_common::error::Error), - #[error("Slot name already exist, choose another name")] - SlotAlreadyExist, - #[error("Publication name already exist, choose another name")] - PublicationAlreadyExist, -} - -trait RowExist { - fn row_exist(&self) -> bool; -} - -impl RowExist for Vec { - fn row_exist(&self) -> bool { - self.iter() - .find_map(|element| { - if let SimpleQueryMessage::CommandComplete(value) = element { - Some(*value) - } else { - None - } - }) - .is_some_and(|value| value > 0) - } -} - -pub struct PostgresSimpleClient(Client); - -impl PostgresSimpleClient { - pub async fn new(database: &Database) -> Result { - let mut config = Config::new(); - - config - .dbname(&database.db_name) - .host(&database.host) - .port(database.port) - .user(&database.username) - .replication_mode(rust_postgres::config::ReplicationMode::Logical); - - if let Some(password) = &database.password { - config.password(password); - } - - let (client, connection) = config.connect(NoTls).await.map_err(Error::Postgres)?; - tokio::spawn(async move { - if let Err(e) = connection.await { - println!("{:#?}", e); - }; - tracing::info!("Successfully Connected into database"); - }); - - Ok(PostgresSimpleClient(client)) - } - - pub async fn get_logical_replication_stream( - &self, - publication_name: &str, - logical_replication_slot_name: &str, - ) -> Result<(CopyBothDuplex, LogicalReplicationSettings), Error> { - let binary_format = true; - let options = match binary_format { - true => format!( - r#"("proto_version" '2', "publication_names" {})"#, - //r#"("proto_version" '2', "publication_names" {}, "binary")"#, - quote_literal(publication_name), - ), - false => format!( - r#"("proto_version" '2', "publication_names" {})"#, - quote_literal(publication_name), - ), - }; - - let query = format!( - r#"START_REPLICATION SLOT {} LOGICAL 0/0 {}"#, - quote_identifier(logical_replication_slot_name), - options - ); - - Ok(( - self.0 - .copy_both_simple::(query.as_str()) - .await - .map_err(Error::Postgres)?, - LogicalReplicationSettings::new(binary_format, false), - )) - } - - pub async fn send_status_update( - primary_keep_alive: PrimaryKeepAliveBody, - copy_both_stream: &mut Pin<&mut CopyBothDuplex>, - ) { - let mut buf = BytesMut::new(); - let ts = chrono::Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); - let ts = chrono::Utc::now() - .signed_duration_since(ts) - .num_microseconds() - .unwrap_or(0); - - buf.put_u8(b'r'); - buf.put_u64(primary_keep_alive.wal_end); - buf.put_u64(primary_keep_alive.wal_end); - buf.put_u64(primary_keep_alive.wal_end); - buf.put_i64(ts); - buf.put_u8(0); - copy_both_stream.send(buf.freeze()).await.unwrap(); - tracing::info!("Send update status message"); - } - - async fn get_slot(&self, slot_name: &str) -> Result { - let query = format!( - r#"select 1 from pg_replication_slots where slot_name = {};"#, - quote_literal(slot_name) - ); - - let query_result = self.0.simple_query(&query).await.map_err(Error::Postgres)?; - - if query_result.row_exist() { - return Ok(true); - } - - Ok(false) - } - - async fn check_if_publication_exists(&self, publication: &str) -> Result { - let publication_exists_query = format!( - "select 1 as exists from pg_publication where pubname = {};", - quote_literal(publication) - ); - let rows = self - .0 - .simple_query(&publication_exists_query) - .await - .map_err(Error::Postgres)?; - for msg in rows { - if let SimpleQueryMessage::Row(_) = msg { - return Ok(true); - } - } - Ok(false) - } -} - -pub struct PostgresExtendedClient(PgConnection); - -impl PostgresExtendedClient { - pub async fn new(db: &Database) -> Result { - let options = { - let options = PgConnectOptions::new() - .host(&db.host) - .database(&db.db_name) - .port(db.port) - .username(&db.username); - - if let Some(password) = &db.password { - options.password(password) - } else { - options - } - }; - - let connection = PgConnection::connect_with(&options) - .await - .map_err(Error::Sqlx)?; - - Ok(PostgresExtendedClient(connection)) - } -} - -impl Deref for PostgresExtendedClient { - type Target = PgConnection; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for PostgresExtendedClient { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} diff --git a/backend/windmill-api/src/database_triggers/handler.rs b/backend/windmill-api/src/database_triggers/handler.rs index 952f468433826..e74eda854011b 100644 --- a/backend/windmill-api/src/database_triggers/handler.rs +++ b/backend/windmill-api/src/database_triggers/handler.rs @@ -9,15 +9,16 @@ use axum::{ }; use http::StatusCode; use itertools::Itertools; -use pg_escape::quote_literal; +use pg_escape::{quote_identifier, quote_literal}; use rust_postgres::types::Type; use serde::{Deserialize, Deserializer, Serialize}; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::{ postgres::{types::Oid, PgConnectOptions}, - Connection, FromRow, PgConnection, + query_as, Connection, Execute, FromRow, PgConnection, QueryBuilder, }; use windmill_audit::{audit_ee::audit_log, ActionKind}; +use windmill_common::error::Error; use windmill_common::{ db::UserDB, error::{self, JsonResult}, @@ -32,8 +33,6 @@ use crate::{ db::{ApiAuthed, DB}, }; -use super::SqlxJson; - #[derive(Clone, Debug, sqlx::Type, Deserialize, Serialize)] #[sqlx(type_name = "transaction")] pub enum TransactionType { @@ -58,12 +57,32 @@ pub struct TableToTrack { pub columns_name: Vec, } +impl TableToTrack { + fn new( + table_name: String, + where_clause: Option, + columns_name: Vec, + ) -> TableToTrack { + TableToTrack { table_name, where_clause, columns_name } + } +} + #[derive(FromRow, Serialize, Deserialize, Debug)] pub struct Relations { pub schema_name: String, pub table_to_track: Vec, } +impl Relations { + fn new(schema_name: String, table_to_track: Vec) -> Relations { + Relations { schema_name, table_to_track } + } + + fn add_new_table(&mut self, table_to_track: TableToTrack) { + self.table_to_track.push(table_to_track); + } +} + #[derive(Deserialize)] pub struct EditDatabaseTrigger { replication_slot_name: String, @@ -94,6 +113,26 @@ pub struct NewDatabaseTrigger { publication_name: String, } +async fn get_raw_postgres_connection(db: &Database) -> Result { + let options = { + let options = PgConnectOptions::new() + .host(&db.host) + .database(&db.db_name) + .port(db.port) + .username(&db.username); + + if let Some(password) = &db.password { + options.password(password) + } else { + options + } + }; + + PgConnection::connect_with(&options) + .await + .map_err(Error::SqlErr) +} + #[derive(Debug)] pub enum Language { Typescript, @@ -177,7 +216,7 @@ where let mut transaction_type: Vec = Vec::deserialize(transaction_type)?; if transaction_type.len() > 3 { return Err(serde::de::Error::custom( - "Only the 3 transaction types are at most allowed: Insert, Update and Delete" + "More than 3 transaction type which is not authorized, you are only allowed to those 3 transaction types: Insert, Update and Delete" .to_string(), )); } @@ -187,13 +226,13 @@ where let mut result = Vec::with_capacity(transaction_type.len()); for transaction in transaction_type { - match transaction.as_str() { - "Insert" => result.push(TransactionType::Insert), - "Update" => result.push(TransactionType::Update), - "Delete" => result.push(TransactionType::Delete), + match transaction.to_lowercase().as_str() { + "insert" => result.push(TransactionType::Insert), + "update" => result.push(TransactionType::Update), + "delete" => result.push(TransactionType::Delete), _ => { return Err(serde::de::Error::custom( - "Only the following transaction types are allowed: Insert, Update and Delete" + "Only the following transaction types are allowed: Insert, Update and Delete (case insensitive)" .to_string(), )) } @@ -222,6 +261,64 @@ pub struct DatabaseTrigger { pub enabled: bool, } +#[derive(Debug, Serialize)] +pub struct DatabaseTriggerResponse { + pub path: String, + pub script_path: String, + pub is_flow: bool, + pub workspace_id: String, + pub edited_by: String, + pub edited_at: chrono::DateTime, + pub database_resource_path: String, + pub error: Option, + pub replication_slot_name: String, + pub publication_name: String, + pub enabled: bool, + pub table_to_track: Option>, + pub transaction_to_track: Vec, +} + +impl DatabaseTriggerResponse { + pub fn new( + database_trigger: DatabaseTrigger, + table_to_track: Option>, + transaction_to_track: Vec, + ) -> DatabaseTriggerResponse { + DatabaseTriggerResponse { + path: database_trigger.path, + script_path: database_trigger.script_path, + is_flow: database_trigger.is_flow, + workspace_id: database_trigger.workspace_id, + edited_by: database_trigger.edited_by, + edited_at: database_trigger.edited_at, + database_resource_path: database_trigger.database_resource_path, + error: database_trigger.error, + replication_slot_name: database_trigger.replication_slot_name, + publication_name: database_trigger.publication_name, + enabled: database_trigger.enabled, + table_to_track, + transaction_to_track, + } + } +} + +pub async fn get_database_resource( + db: &DB, + database_resource_path: &str, + w_id: &str, +) -> Result { + let mut resource = get_resource::(db, database_resource_path, w_id) + .await + .map_err(Error::SqlErr)?; + + if resource.value.password.is_some() { + let password = get_variable_or_self(resource.value.password.unwrap(), db, w_id).await?; + resource.value.password = Some(password) + } + + Ok(resource.value) +} + #[derive(Deserialize, Serialize)] pub struct ListDatabaseTriggerQuery { pub page: Option, @@ -240,6 +337,7 @@ pub async fn create_database_trigger( authed: ApiAuthed, Extension(user_db): Extension, Path(w_id): Path, + Extension(db): Extension, Json(new_database_trigger): Json, ) -> error::Result<(StatusCode, String)> { let NewDatabaseTrigger { @@ -259,298 +357,83 @@ pub async fn create_database_trigger( )); } - /* - - - async fn atler_publication( - publication_name: &str, - relations: Option<&[Relations]>, - transaction_to_track: Option<&[TransactionType]>, - ) -> Result<(), Error> { - let mut query = String::new(); - let quoted_publication_name = quote_identifier(publication_name); - match relations { - Some(relations) if !relations.is_empty() => { - query.push_str("ALTER PUBLICATION "); - query.push_str("ed_publication_name); - query.push_str(" SET"); - for (i, relation) in relations.iter().enumerate() { - if !relation - .table_to_track - .iter() - .any(|table| !table.table_name.is_empty()) - { - query.push_str(" TABLES IN SCHEMA "); - let quoted_schema = quote_identifier(&relation.schema_name); - query.push_str("ed_schema); - } else { - query.push_str(" TABLE ONLY "); - for (j, table) in relation - .table_to_track - .iter() - .filter(|table| !table.table_name.is_empty()) - .enumerate() - { - let quoted_table = quote_identifier(&table.table_name); - query.push_str("ed_table); - if !table.columns_name.is_empty() { - query.push_str(" ("); - let columns = table.columns_name.iter().join(","); - query.push_str(&columns); - query.push_str(") "); - } - - if let Some(where_clause) = &table.where_clause { - //query.push_str("WHERE "); - } - - if j + 1 != relation.table_to_track.len() { - query.push_str(", ") - } - } - } - if i < relations.len() - 1 { - query.push(',') - } - } - } - _ => { - let to_execute = format!( - r#" - DROP - PUBLICATION {}; - CREATE - PUBLICATION {} FOR ALL TABLES - "#, - quoted_publication_name, quoted_publication_name - ); - query.push_str(&to_execute); - } - }; - tracing::info!("query: {}", query); - if let Some(transaction_to_track) = transaction_to_track { - query.push_str("; ALTER PUBLICATION "); - query.push_str("ed_publication_name); - if !transaction_to_track.is_empty() { - let transactions = || { - transaction_to_track - .iter() - .map(|transaction| match transaction { - TransactionType::Insert => "insert", - TransactionType::Update => "update", - TransactionType::Delete => "delete", - }) - .join(",") - }; - let with_parameter = format!(" SET (publish = '{}'); ", transactions()); - query.push_str(&with_parameter); - } else { - query.push_str(" SET (publish = 'insert,update,delete')"); - } - } - - self.client - .simple_query(&query) - .await - .map_err(Error::Postgres)?; - - Ok(()) - } - - async fn create_slot(&self, slot_name: &str) -> Result<(), Error> { - let query = format!( - "SELECT * FROM pg_create_logical_replication_slot({}, 'pgoutput')", - quote_literal(slot_name) - ); - self.client - .simple_query(&query) - .await - .map_err(Error::Postgres)?; - Ok(()) - } + let database = get_database_resource(&db, &database_resource_path, &w_id).await?; + let mut connection = get_raw_postgres_connection(&database).await?; - pub async fn check_if_table_exists( - &self, - table_to_track: &[&str], - catalog: &str, - db_name: &str, - ) -> Result<(), Error> { - let table_names = table_to_track - .iter() - .map(|table| quote_literal(table)) - .join(","); + let mut query = QueryBuilder::new(""); - let query = format!( - r#" - WITH target_tables AS ( - SELECT unnest(ARRAY[{}]) AS table_name - ) - SELECT t.table_name - FROM - target_tables t - LEFT JOIN - information_schema.tables ist - ON - t.table_name = ist.table_name - AND ist.table_type = 'BASE TABLE' - AND ist.table_catalog = {} - AND ist.table_schema NOT IN ('pg_catalog', 'information_schema') - AND ist.table_schema = {} - WHERE - ist.table_name IS NULL; - "#, - table_names, - quote_literal(db_name), - quote_literal(catalog) - ); - - let rows = self - .client - .simple_query(&query) - .await - .map_err(Error::Postgres)?; - - if !rows.row_exist() { - return Ok(()); - } + query.push("CREATE PUBLICATION "); + query.push(quote_identifier(&publication_name)); - Err(Error::MissingTables( - rows.into_iter() - .filter_map(|row| { - if let SimpleQueryMessage::Row(row) = row { - return Some(row.get("table_name").unwrap().to_string()); - } - None - }) - .collect_vec() - .join(", "), - )) - } + match table_to_track.as_ref() { + Some(database_component) if !database_component.is_empty() => { + query.push(" FOR"); + for (i, schema) in database_component.iter().enumerate() { + if schema.table_to_track.is_empty() { + query.push(" TABLES IN SCHEMA "); + query.push(quote_identifier(&schema.schema_name)); + } else { + query.push(" TABLE ONLY "); + for (j, table) in schema.table_to_track.iter().enumerate() { + query.push(quote_identifier(&table.table_name)); + if !table.columns_name.is_empty() { + query.push(" ("); + let columns = table + .columns_name + .iter() + .map(|column| quote_identifier(column)) + .join(", "); + query.push(&columns); + query.push(")"); + } - async fn create_publication( - &self, - publication_name: &str, - relations: Option<&[Relations]>, - transaction_to_track: Option<&[TransactionType]>, - ) -> Result<(), Error> { - let mut query = String::new(); - let quoted_publication_name = quote_identifier(publication_name); - query.push_str("CREATE PUBLICATION "); - query.push_str("ed_publication_name); - - match relations { - Some(relations) if !relations.is_empty() => { - query.push_str(" FOR "); - for (i, relation) in relations.iter().enumerate() { - if !relation - .table_to_track - .iter() - .any(|table| !table.table_name.is_empty()) - { - query.push_str(" TABLES IN SCHEMA "); - let quoted_schema = quote_identifier(&relation.schema_name); - query.push_str("ed_schema); - } else { - query.push_str(" TABLE ONLY "); - for (j, table) in relation - .table_to_track - .iter() - .filter(|table| !table.table_name.is_empty()) - .enumerate() - { - let quoted_table = quote_identifier(&table.table_name); - query.push_str("ed_table); - if !table.columns_name.is_empty() { - query.push_str(" ("); - let columns = table.columns_name.iter().join(","); - query.push_str(&columns); - query.push(')'); - } - if j + 1 != relation.table_to_track.len() { - query.push_str(", ") - } + if j + 1 != schema.table_to_track.len() { + query.push(", "); } } - if i < relations.len() - 1 { - query.push(',') - } } - } - _ => query.push_str(" FOR ALL TABLES "), - }; - - if let Some(transaction_to_track) = transaction_to_track { - if !transaction_to_track.is_empty() { - let transactions = || { - transaction_to_track - .iter() - .map(|transaction| match transaction { - TransactionType::Insert => "insert", - TransactionType::Update => "update", - TransactionType::Delete => "delete", - }) - .join(',') - }; - let with_parameter = format!(" WITH (publish = '{}'); ", transactions()); - query.push_str(&with_parameter); + if i < database_component.len() - 1 { + query.push(", "); + } } } + _ => { + query.push(" FOR ALL TABLES "); + } + }; - self.client - .simple_query(&query) - .await - .map_err(Error::Postgres)?; - - Ok(()) - } - - let table_to_track = if let Some(table_to_track) = &database_trigger.table_to_track { - for relation in table_to_track.0.iter() { - let tables = relation - .table_to_track + if !transaction_to_track.is_empty() { + let transactions = || { + transaction_to_track .iter() - .filter_map(|table_to_track| { - if table_to_track.table_name.is_empty() { - None - } else { - Some(table_to_track.table_name.as_str()) - } + .map(|transaction| match transaction { + TransactionType::Insert => "insert", + TransactionType::Update => "update", + TransactionType::Delete => "delete", }) - .collect_vec(); - if tables.is_empty() { - continue; - } - client - .check_if_table_exists( - tables.as_slice(), - &relation.schema_name, - &resource.value.db_name, - ) - .await?; - } - Some(table_to_track) - } else { - None - }; + .join(",") + }; + query.push(" WITH (publish = '"); + query.push(transactions()); + query.push("');"); + } - tracing::info!("Starting tokio select futures"); + let query = query.build(); - client - .get_or_create_slot(&database_trigger.replication_slot_name) - .await?; + println!("{}", query.sql()); - client - .create_publication_if_not_exist( - &database_trigger.publication_name, - table_to_track.map(|v| &***v), - database_trigger.transaction_to_track.as_deref(), - ) - .await?; - */ - let mut tx = user_db.begin(&authed).await?; + query.execute(&mut connection).await?; - let table_to_track = serde_json::to_value(table_to_track).unwrap(); + sqlx::query!( + "SELECT + pg_create_logical_replication_slot($1, 'pgoutput')", + &replication_slot_name + ) + .fetch_optional(&mut connection) + .await?; + + let mut tx = user_db.begin(&authed).await?; sqlx::query!( r#" @@ -621,7 +504,6 @@ pub async fn list_database_triggers( let mut sqlb = SqlBuilder::select_from("database_trigger") .fields(&[ "workspace_id", - "transaction_to_track", "path", "script_path", "is_flow", @@ -636,7 +518,6 @@ pub async fn list_database_triggers( "database_resource_path", "replication_slot_name", "publication_name", - "table_to_track", ]) .order_by("edited_at", true) .and_where("workspace_id = ?".bind(&w_id)) @@ -670,13 +551,62 @@ pub async fn list_database_triggers( Ok(Json(rows)) } +async fn get_publication_scope_and_transaction( + publication_name: &str, + connection: &mut PgConnection, +) -> Result<(bool, Vec), Error> { + #[derive(Debug, Deserialize, FromRow)] + struct PublicationTransaction { + all_table: bool, + insert: bool, + update: bool, + delete: bool, + } + + let transaction = sqlx::query_as!( + PublicationTransaction, + r#" + SELECT + puballtables AS all_table, + pubinsert AS insert, + pubupdate AS update, + pubdelete AS delete + FROM + pg_publication + WHERE + pubname = $1 + "#, + publication_name + ) + .fetch_one(&mut *connection) + .await?; + + let mut transaction_to_track = Vec::with_capacity(3); + + if transaction.insert { + transaction_to_track.push("insert".to_string()); + } + if transaction.update { + transaction_to_track.push("update".to_string()); + } + if transaction.delete { + transaction_to_track.push("delete".to_string()); + } + + tracing::info!("{:#?}", &transaction); + + Ok((transaction.all_table, transaction_to_track)) +} + pub async fn get_database_trigger( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, -) -> error::JsonResult { +) -> JsonResult { let mut tx = user_db.begin(&authed).await?; let path = path.to_path(); + tracing::info!("inside"); let trigger = sqlx::query_as!( DatabaseTrigger, r#" @@ -711,12 +641,75 @@ pub async fn get_database_trigger( let trigger = not_found_if_none(trigger, "Trigger", path)?; - Ok(Json(trigger)) + let database = get_database_resource(&db, &trigger.database_resource_path, &w_id).await?; + + let mut connection = get_raw_postgres_connection(&database).await?; + + let (all_table, transaction_to_track) = + get_publication_scope_and_transaction(&trigger.publication_name, &mut connection).await?; + + #[derive(Debug, Deserialize, FromRow)] + struct PublicationData { + schema_name: Option, + table_name: Option, + columns: Option>, + where_clause: Option, + } + + let table_to_track = if !all_table { + let publications = sqlx::query_as!( + PublicationData, + r#" + SELECT + schemaname AS schema_name, + tablename AS table_name, + attnames AS columns, + rowfilter AS where_clause + FROM + pg_publication_tables + WHERE + pubname = $1 + "#, + &trigger.publication_name + ) + .fetch_all(&mut connection) + .await?; + + let mut table_to_track: HashMap = HashMap::new(); + + for publication in publications { + let schema_name = publication.schema_name.unwrap(); + let entry = table_to_track.entry(schema_name.clone()); + let table_to_track = TableToTrack::new( + publication.table_name.unwrap(), + publication.where_clause, + publication.columns.unwrap(), + ); + match entry { + Occupied(mut occuped) => { + occuped.get_mut().add_new_table(table_to_track); + } + Vacant(vacant) => { + vacant.insert(Relations::new(schema_name, vec![table_to_track])); + } + } + } + Some(table_to_track.into_values().collect_vec()) + } else { + None + }; + + tracing::info!("{:#?}", &table_to_track); + + let response = DatabaseTriggerResponse::new(trigger, table_to_track, transaction_to_track); + + Ok(Json(response)) } pub async fn update_database_trigger( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, Json(database_trigger): Json, ) -> error::Result { @@ -731,9 +724,111 @@ pub async fn update_database_trigger( table_to_track, transaction_to_track, } = database_trigger; - let mut tx = user_db.begin(&authed).await?; - let table_to_track = serde_json::to_value(table_to_track).unwrap(); + let database = get_database_resource(&db, &database_resource_path, &w_id).await?; + + let mut connection = get_raw_postgres_connection(&database).await?; + + let (all_table, _) = + get_publication_scope_and_transaction(&publication_name, &mut connection).await?; + + let mut query = QueryBuilder::new(""); + let quoted_publication_name = quote_identifier(&publication_name); + + async fn drop_publication( + query: &mut QueryBuilder<'_, sqlx::Postgres>, + quoted_publication_name: &str, + connection: &mut PgConnection, + ) -> Result<(), Error> { + query.push("DROP PUBLICATION "); + query.push(" IF EXISTS "); + query.push(quoted_publication_name); + query.push(";"); + query.build().execute(&mut *connection).await?; + query.reset(); + Ok(()) + } + + match table_to_track { + Some(relations) if !relations.is_empty() => { + if all_table { + return Err(Error::ExecutionErr("Publication that once track all table must be deleted manually if the tracking is now specific table/schema".to_string())); + } + query.push("ALTER PUBLICATION "); + query.push("ed_publication_name); + query.push(" SET"); + for (i, relation) in relations.iter().enumerate() { + if relation.table_to_track.is_empty() { + query.push(" TABLES IN SCHEMA "); + let quoted_schema = quote_identifier(&relation.schema_name); + query.push("ed_schema); + } else { + query.push(" TABLE ONLY "); + for (j, table) in relation.table_to_track.iter().enumerate() { + let quoted_table = quote_identifier(&table.table_name); + query.push("ed_table); + if !table.columns_name.is_empty() { + query.push(" ("); + let columns = table + .columns_name + .iter() + .map(|column| quote_identifier(column)) + .join(", "); + query.push(&columns); + query.push(") "); + } + + if let Some(where_clause) = &table.where_clause { + //query.push_str("WHERE "); + } + + if j + 1 != relation.table_to_track.len() { + query.push(", "); + } + } + } + if i < relations.len() - 1 { + query.push(','); + } + } + } + _ => { + drop_publication(&mut query, "ed_publication_name, &mut connection).await?; + let to_execute = format!( + r#" + CREATE + PUBLICATION {} FOR ALL TABLES + "#, + quoted_publication_name + ); + query.push(&to_execute); + } + }; + + query.push(";"); + query.build().execute(&mut connection).await?; + query.reset(); + query.push("ALTER PUBLICATION "); + query.push("ed_publication_name); + if !transaction_to_track.is_empty() { + let transactions = || { + transaction_to_track + .iter() + .map(|transaction| match transaction { + TransactionType::Insert => "insert", + TransactionType::Update => "update", + TransactionType::Delete => "delete", + }) + .join(",") + }; + let with_parameter = format!(" SET (publish = '{}'); ", transactions()); + query.push(&with_parameter); + } else { + query.push(" SET (publish = 'insert,update,delete')"); + } + query.build().execute(&mut connection).await?; + + let mut tx = user_db.begin(&authed).await?; sqlx::query!( r#" diff --git a/backend/windmill-api/src/database_triggers/mod.rs b/backend/windmill-api/src/database_triggers/mod.rs index 903184701572f..d1aeb9e37f7ee 100644 --- a/backend/windmill-api/src/database_triggers/mod.rs +++ b/backend/windmill-api/src/database_triggers/mod.rs @@ -27,9 +27,7 @@ mod mapper; mod relation; mod replication_message; mod trigger; -mod client; -pub type SqlxJson = sqlx::types::Json; pub use trigger::start_database; pub fn workspaced_service() -> Router { diff --git a/backend/windmill-api/src/database_triggers/trigger.rs b/backend/windmill-api/src/database_triggers/trigger.rs index 042f15719e800..cd3e7845f43d8 100644 --- a/backend/windmill-api/src/database_triggers/trigger.rs +++ b/backend/windmill-api/src/database_triggers/trigger.rs @@ -1,8 +1,8 @@ -use std::collections::HashMap; +use std::{collections::HashMap, pin::Pin}; use crate::{ database_triggers::{ - client::{Error, PostgresSimpleClient}, + handler::get_database_resource, relation::RelationConverter, replication_message::{ LogicalReplicationMessage::{Begin, Commit, Delete, Insert, Relation, Type, Update}, @@ -12,13 +12,18 @@ use crate::{ }, db::DB, }; -use futures::{pin_mut, StreamExt}; +use bytes::{BufMut, Bytes, BytesMut}; +use chrono::TimeZone; +use futures::{pin_mut, SinkExt, StreamExt}; +use pg_escape::{quote_identifier, quote_literal}; use rand::seq::SliceRandom; -use windmill_common::{ - resource::get_resource, variables::get_variable_or_self, worker::to_raw_value, INSTANCE_NAME, -}; +use rust_postgres::{Client, Config, CopyBothDuplex, NoTls, SimpleQueryMessage}; +use windmill_common::{worker::to_raw_value, INSTANCE_NAME}; -use super::handler::{Database, DatabaseTrigger}; +use super::{ + handler::{Database, DatabaseTrigger}, + replication_message::PrimaryKeepAliveBody, +}; pub struct LogicalReplicationSettings { pub streaming: bool, @@ -31,6 +36,120 @@ impl LogicalReplicationSettings { } } +trait RowExist { + fn row_exist(&self) -> bool; +} + +impl RowExist for Vec { + fn row_exist(&self) -> bool { + self.iter() + .find_map(|element| { + if let SimpleQueryMessage::CommandComplete(value) = element { + Some(*value) + } else { + None + } + }) + .is_some_and(|value| value > 0) + } +} + +#[derive(thiserror::Error, Debug)] +enum Error { + #[error("Error from database: {0}")] + Postgres(rust_postgres::Error), + #[error("Error : {0}")] + Common(windmill_common::error::Error), +} + +pub struct PostgresSimpleClient(Client); + +impl PostgresSimpleClient { + async fn new(database: &Database) -> Result { + let mut config = Config::new(); + + config + .dbname(&database.db_name) + .host(&database.host) + .port(database.port) + .user(&database.username) + .replication_mode(rust_postgres::config::ReplicationMode::Logical); + + if let Some(password) = &database.password { + config.password(password); + } + + let (client, connection) = config.connect(NoTls).await.map_err(Error::Postgres)?; + tokio::spawn(async move { + if let Err(e) = connection.await { + println!("{:#?}", e); + }; + tracing::info!("Successfully Connected into database"); + }); + + Ok(PostgresSimpleClient(client)) + } + + async fn get_logical_replication_stream( + &self, + publication_name: &str, + logical_replication_slot_name: &str, + ) -> Result<(CopyBothDuplex, LogicalReplicationSettings), Error> { + let binary_format = true; + let options = match binary_format { + true => format!( + r#"("proto_version" '2', "publication_names" {})"#, + //r#"("proto_version" '2', "publication_names" {}, "binary")"#, + quote_literal(publication_name), + ), + false => format!( + r#"("proto_version" '2', "publication_names" {})"#, + quote_literal(publication_name), + ), + }; + + let query = format!( + r#"START_REPLICATION SLOT {} LOGICAL 0/0 {}"#, + quote_identifier(logical_replication_slot_name), + options + ); + + Ok(( + self.0 + .copy_both_simple::(query.as_str()) + .await + .map_err(Error::Postgres)?, + LogicalReplicationSettings::new(binary_format, false), + )) + } + + async fn send_status_update( + primary_keep_alive: PrimaryKeepAliveBody, + copy_both_stream: &mut Pin<&mut CopyBothDuplex>, + ) { + let mut buf = BytesMut::new(); + let ts = chrono::Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let ts = chrono::Utc::now() + .signed_duration_since(ts) + .num_microseconds() + .unwrap_or(0); + + buf.put_u8(b'r'); + buf.put_u64(primary_keep_alive.wal_end); + buf.put_u64(primary_keep_alive.wal_end); + buf.put_u64(primary_keep_alive.wal_end); + buf.put_i64(ts); + buf.put_u8(0); + copy_both_stream.send(buf.freeze()).await.unwrap(); + tracing::info!("Send update status message"); + } + + async fn check_if_row_exists(&self, query: &str) -> Result { + let rows = self.0.simple_query(query).await.map_err(Error::Postgres)?; + Ok(rows.row_exist()) + } +} + async fn update_ping( db: &DB, database_trigger: &DatabaseTrigger, @@ -95,26 +214,15 @@ async fn listen_to_transactions( rsmq: Option, mut killpill_rx: tokio::sync::broadcast::Receiver<()>, ) -> Result<(), Error> { - let mut resource = get_resource::( + let resource = get_database_resource( &db, &database_trigger.database_resource_path, &database_trigger.workspace_id, ) .await - .map_err(Error::Sqlx)?; - - if resource.value.password.is_some() { - let password = get_variable_or_self( - resource.value.password.unwrap(), - &db, - &database_trigger.workspace_id, - ) - .await - .map_err(Error::CommonError)?; - resource.value.password = Some(password) - } + .map_err(Error::Common)?; - let client = PostgresSimpleClient::new(&resource.value).await?; + let client = PostgresSimpleClient::new(&resource).await?; let (logical_replication_stream, logicail_replication_settings) = client .get_logical_replication_stream( @@ -380,7 +488,7 @@ pub async fn start_database( rsmq: Option, mut killpill_rx: tokio::sync::broadcast::Receiver<()>, ) { - tokio::spawn(async move { + /*tokio::spawn(async move { listen_to_unlistened_database_events(&db, &rsmq, &killpill_rx).await; loop { tokio::select! { @@ -393,5 +501,5 @@ pub async fn start_database( } } } - }); + });*/ } diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index f48d343fa59fa..21ea3aa2b3e09 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -359,84 +359,85 @@

- {#each relations as v, i} -
-
- - {#each v.table_to_track as table_to_track, j} -
-
- - - - - + {#if relations && relations.length > 0} + {#each relations as v, i} +
+
+ + {#each v.table_to_track as table_to_track, j} +
+
+ + + + + +
-
- {/each} - +
+ + +
- -
- {/each} - + {/each} + {/if}
+ {/if} + + {#if drawerLoading} + + {:else} +
+
+

+ Choose what kind of database transaction you want to track allowed operations are + Insert, Update, Delete +

+ + +
+ +
+
+
+

+ Choose a slot name +

+ +
+
+
+ +
+
+ { + publication_name = '' + relations = [] + }} + > + + + + + {#if selected === 'create'} +
+ + +
+ {:else} + + {/if} + + { + if (selectedTable === 'all') { + relations = [] + } + }} + > + + + + + {#if selectedTable != 'all' && (selected === 'create' || (items.length > 0 && !emptyString(publication_name)))} +
+ {#if relations && relations.length > 0} + {#each relations as v, i} +
+
+ + {#each v.table_to_track as table_to_track, j} +
+
+ + + + + +
+
+ {/each} + +
+ +
+ {/each} + {/if} +
+ +
+
+ {/if} +
+
+
+ {/if} + + +{/if} diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index c49ecf90c181b..006f39d33f194 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -15,14 +15,14 @@ import { canWrite, emptyString, sendUserToast } from '$lib/utils' import { createEventDispatcher } from 'svelte' import Section from '$lib/components/Section.svelte' - import { Loader2, Plus, Save, X } from 'lucide-svelte' + import { Loader2, Save } from 'lucide-svelte' import Label from '$lib/components/Label.svelte' import Toggle from '$lib/components/Toggle.svelte' import ResourcePicker from '$lib/components/ResourcePicker.svelte' - import MultiSelect from 'svelte-multiselect' - import { fade } from 'svelte/transition' import { goto } from '$app/navigation' import { base } from '$app/paths' + import ConfigurationEditor from './ConfigurationEditor.svelte' + import Tooltip from '$lib/components/Tooltip.svelte' let drawer: Drawer let is_flow: boolean = false @@ -42,11 +42,11 @@ let publication_name: string = '' let replication_slot_name: string = '' let relations: Relations[] = [] - let transactionType: TransactionType[] = ['Insert', 'Update', 'Delete'] - let transactionToTrack: TransactionType[] = [] + let transaction_to_track: TransactionType[] = [] let languages = 'bun,deno' let language: Language = 'Typescript' let loading = false + let configurationEditor: ConfigurationEditor const dispatch = createEventDispatcher() $: is_flow = itemKind === 'flow' @@ -114,7 +114,7 @@ enabled = s.enabled database_resource_path = s.database_resource_path relations = s.table_to_track as Relations[] - transactionToTrack = s.transaction_to_track + transaction_to_track = s.transaction_to_track publication_name = s.publication_name replication_slot_name = s.replication_slot_name can_write = canWrite(s.path, s.extra_perms, $userStore) @@ -132,7 +132,7 @@ database_resource_path, enabled, replication_slot_name, - publication_name, + publication_name } }) sendUserToast(`Database ${path} updated`) @@ -146,7 +146,7 @@ enabled: true, database_resource_path, replication_slot_name, - publication_name, + publication_name } }) sendUserToast(`Database ${path} created`) @@ -188,7 +188,7 @@ enabled: true, database_resource_path, replication_slot_name, - publication_name, + publication_name } }) await goto(`${base}/scripts/add`) @@ -197,8 +197,18 @@ console.log({ error }) } } + + +
-
-

- Choose what kind of database transaction you want to track allowed operations are - Insert, Update, Delete +

+

+ Choose which table of your database to track as well as what kind of transaction should + fire the script.
+ You must pick a database resource first to make the configuration of your trigger +

- - +
@@ -311,148 +322,25 @@ /> {#if script_path === undefined && is_flow === false} - +
+ + To enable that features,Select a database resource, and inside database config + create or get a publication from your database +
{/if}
- -
-
-
-

- Choose a publication name -

- -
-
-

- Choose a slot name -

- -
-
-
- -
-

- Relations will limit the execution of the trigger to only the specified tables.
- If no tables are selected, this will trigger for all tables.
-

- -
- {#if relations && relations.length > 0} - {#each relations as v, i} -
-
- - {#each v.table_to_track as table_to_track, j} -
-
- - - - - -
-
- {/each} - -
- -
- {/each} - {/if} -
- -
-
{/if} diff --git a/frontend/src/lib/components/triggers/database_triggers/PublicationPicker.svelte b/frontend/src/lib/components/triggers/database_triggers/PublicationPicker.svelte new file mode 100644 index 0000000000000..a4daaf58884bd --- /dev/null +++ b/frontend/src/lib/components/triggers/database_triggers/PublicationPicker.svelte @@ -0,0 +1,116 @@ + + + + +
+ -
+
+ + + + + {#if selectedSlotAction === 'create'} +
+ + +
+ {:else} + + {/if}
@@ -124,10 +153,11 @@ on:selected={() => { publication_name = '' relations = [] + transaction_to_track = [] }} > - - + + {#if selected === 'create'} @@ -142,7 +172,7 @@ size="xs" variant="border" disabled={emptyString(publication_name)} - on:click={() => createPublication()}>CreateCreate {:else} @@ -156,16 +186,9 @@ /> {/if} - { - if (selectedTable === 'all') { - relations = [] - } - }} - > - - + + + {#if selectedTable != 'all' && (selected === 'create' || (items.length > 0 && !emptyString(publication_name)))} diff --git a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte index 006f39d33f194..a1d929fda101e 100644 --- a/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/database_triggers/DatabaseTriggerEditorInner.svelte @@ -197,13 +197,12 @@ console.log({ error }) } } -

- Pick a database to connect to + Pick a database to connect to

@@ -307,7 +306,7 @@

- Pick a script or flow to be triggered + Pick a script or flow to be triggered

Create from template { - getAllRelations() - }} + on:select={getAllRelations} + /> + Update Delete
diff --git a/frontend/src/lib/components/triggers/database_triggers/SlotPicker.svelte b/frontend/src/lib/components/triggers/database_triggers/SlotPicker.svelte new file mode 100644 index 0000000000000..4b4f472305716 --- /dev/null +++ b/frontend/src/lib/components/triggers/database_triggers/SlotPicker.svelte @@ -0,0 +1,79 @@ + + + + +
+ Create
@@ -180,7 +228,7 @@ {database_resource_path} bind:transaction_to_track bind:table_to_track={relations} - bind:items + bind:items={publicationItems} bind:publication_name bind:selectedTable /> @@ -191,7 +239,7 @@ - {#if selectedTable != 'all' && (selected === 'create' || (items.length > 0 && !emptyString(publication_name)))} + {#if showAddSchema}
{#if relations && relations.length > 0} {#each relations as v, i} @@ -211,10 +259,12 @@
{/if} - {#if $databaseTrigger?.databaseTrigger} - - {/if} From 348922dfd9795b9698aefd16d220407a2ba398f2 Mon Sep 17 00:00:00 2001 From: dieriba Date: Tue, 24 Dec 2024 23:55:22 +0100 Subject: [PATCH 37/50] feat: --- backend/Cargo.lock | 5 +- backend/Cargo.toml | 2 +- backend/windmill-api/openapi.yaml | 110 +----------------- backend/windmill-api/src/apps.rs | 2 +- .../src/database_triggers/mapper.rs | 4 +- .../windmill-api/src/database_triggers/mod.rs | 5 +- .../src/database_triggers/trigger.rs | 19 +-- backend/windmill-api/src/lib.rs | 22 ++-- backend/windmill-api/src/workspaces.rs | 15 +-- backend/windmill-api/src/workspaces_export.rs | 9 +- backend/windmill-common/src/lib.rs | 20 ++-- backend/windmill-common/src/scripts.rs | 29 +---- frontend/openapi-ts-error-1732381784283.log | 28 ----- frontend/openapi-ts-error-1732447138336.log | 12 -- frontend/openapi-ts-error-1733590385427.log | 5 - frontend/openapi-ts-error-1734292309527.log | 28 ----- frontend/openapi-ts-error-1734309008594.log | 12 -- frontend/openapi-ts-error-1734309033613.log | 12 -- frontend/openapi-ts-error-1734741871024.log | 28 ----- frontend/openapi-ts-error-1734741998365.log | 28 ----- frontend/openapi-ts-error-1734742008826.log | 12 -- frontend/openapi-ts-error-1734793209662.log | 28 ----- frontend/openapi-ts-error-1734795515701.log | 12 -- frontend/package-lock.json | 14 +++ frontend/package.json | 2 + .../src/lib/components/AuthSettings.svelte | 2 +- .../src/lib/components/InstanceSetting.svelte | 2 +- .../apps/editor/AppEditorHeader.svelte | 2 +- .../database_triggers/SlotPicker.svelte | 2 +- .../http_triggers/RouteEditorInner.svelte | 6 +- .../triggers/http_triggers/RoutesPanel.svelte | 12 +- .../(root)/(logged)/run/[...run]/+page.svelte | 2 +- .../(root)/(logged)/scripts/add/+page.svelte | 2 - 33 files changed, 66 insertions(+), 427 deletions(-) delete mode 100644 frontend/openapi-ts-error-1732381784283.log delete mode 100644 frontend/openapi-ts-error-1732447138336.log delete mode 100644 frontend/openapi-ts-error-1733590385427.log delete mode 100644 frontend/openapi-ts-error-1734292309527.log delete mode 100644 frontend/openapi-ts-error-1734309008594.log delete mode 100644 frontend/openapi-ts-error-1734309033613.log delete mode 100644 frontend/openapi-ts-error-1734741871024.log delete mode 100644 frontend/openapi-ts-error-1734741998365.log delete mode 100644 frontend/openapi-ts-error-1734742008826.log delete mode 100644 frontend/openapi-ts-error-1734793209662.log delete mode 100644 frontend/openapi-ts-error-1734795515701.log diff --git a/backend/Cargo.lock b/backend/Cargo.lock index f44872610e812..5239324ef95be 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -920,7 +920,6 @@ checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", - "axum-macros", "bytes", "futures-util", "http 1.2.0", @@ -9516,7 +9515,7 @@ dependencies = [ "postgres-protocol 0.6.7 (git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b)", "postgres-types 0.2.7", "rand 0.8.5", - "socket2 0.5.7", + "socket2", "tokio", "tokio-util", "whoami", @@ -10821,7 +10820,7 @@ dependencies = [ "sql-builder", "sqlx", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.9", "time", "tinyvector", "tokenizers", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index a82382168be60..2065925902a03 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -155,7 +155,7 @@ windmill-parser-php = { path = "./parsers/windmill-parser-php" } windmill-api-client = { path = "./windmill-api-client" } memchr = "2.7.4" -axum = { version = "^0.7", features = ["multipart", "macros"] } +axum = { version = "^0.7", features = ["multipart"] } headers = "^0" hyper = { version = "^1", features = ["full"] } tokio = { version = "^1.42.0", features = ["full", "tracing"] } diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index f1d2109304c20..058cb23b4a762 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -9192,7 +9192,6 @@ paths: http_trigger, websocket_trigger, kafka_trigger, - database_trigger database_trigger, ] responses: @@ -12002,6 +12001,7 @@ components: php, rust, ansible, + csharp ] Preview: @@ -12988,114 +12988,6 @@ components: - topics - is_flow - KafkaTrigger: - type: object - properties: - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - script_path: - type: string - kafka_resource_path: - type: string - group_id: - type: string - topics: - type: array - items: - type: string - is_flow: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - email: - type: string - workspace_id: - type: string - server_id: - type: string - last_server_ping: - type: string - format: date-time - error: - type: string - enabled: - type: boolean - - required: - - path - - edited_by - - edited_at - - script_path - - kafka_resource_path - - group_id - - topics - - extra_perms - - is_flow - - email - - workspace_id - - enabled - - NewKafkaTrigger: - type: object - properties: - path: - type: string - script_path: - type: string - is_flow: - type: boolean - kafka_resource_path: - type: string - group_id: - type: string - topics: - type: array - items: - type: string - enabled: - type: boolean - - required: - - path - - script_path - - is_flow - - kafka_resource_path - - group_id - - topics - - EditKafkaTrigger: - type: object - properties: - kafka_resource_path: - type: string - group_id: - type: string - topics: - type: array - items: - type: string - path: - type: string - script_path: - type: string - is_flow: - type: boolean - - required: - - path - - script_path - - kafka_resource_path - - group_id - - topics - - is_flow - Group: type: object properties: diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 7f3104df6ceb1..ef3471fb52ece 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -60,7 +60,7 @@ use windmill_common::{ http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath, }, - variables::{build_crypt, build_crypt_with_key_suffix}, + variables::{build_crypt, build_crypt_with_key_suffix, encrypt}, worker::{to_raw_value, CLOUD_HOSTED}, HUB_BASE_URL, }; diff --git a/backend/windmill-api/src/database_triggers/mapper.rs b/backend/windmill-api/src/database_triggers/mapper.rs index 17bd46c362657..acbafb8284916 100644 --- a/backend/windmill-api/src/database_triggers/mapper.rs +++ b/backend/windmill-api/src/database_triggers/mapper.rs @@ -7,8 +7,8 @@ use super::handler::Language; fn postgres_to_typescript_type(postgres_type: Option) -> String { let data_type = match postgres_type { Some(postgres_type) => match postgres_type { - Type::BOOL => "bool", - Type::BOOL_ARRAY => "Array", + Type::BOOL => "boolean", + Type::BOOL_ARRAY => "Array", Type::CHAR | Type::BPCHAR | Type::VARCHAR | Type::NAME | Type::TEXT => "string", Type::CHAR_ARRAY | Type::BPCHAR_ARRAY diff --git a/backend/windmill-api/src/database_triggers/mod.rs b/backend/windmill-api/src/database_triggers/mod.rs index 79c1e78bf21f4..2d0950c04d3ad 100644 --- a/backend/windmill-api/src/database_triggers/mod.rs +++ b/backend/windmill-api/src/database_triggers/mod.rs @@ -68,7 +68,6 @@ async fn run_job( args: Option>>, extra: Option>>, db: &DB, - rsmq: Option, trigger: &DatabaseTrigger, ) -> anyhow::Result<()> { let args = PushArgsOwned { args: args.unwrap_or_default(), extra }; @@ -80,7 +79,7 @@ async fn run_job( trigger.email.clone(), &trigger.workspace_id, db, - "anonymous".to_string(), + Some("anonymous".to_string()), ) .await?; @@ -93,7 +92,6 @@ async fn run_job( authed, db.clone(), user_db, - rsmq, trigger.workspace_id.clone(), StripPath(trigger.script_path.to_owned()), run_query, @@ -106,7 +104,6 @@ async fn run_job( authed, db.clone(), user_db, - rsmq, trigger.workspace_id.clone(), StripPath(trigger.script_path.to_owned()), run_query, diff --git a/backend/windmill-api/src/database_triggers/trigger.rs b/backend/windmill-api/src/database_triggers/trigger.rs index 0d88700db1522..d2b568aaeb3b4 100644 --- a/backend/windmill-api/src/database_triggers/trigger.rs +++ b/backend/windmill-api/src/database_triggers/trigger.rs @@ -206,7 +206,6 @@ async fn loop_ping(db: &DB, database_trigger: &DatabaseTrigger, error: Option<&s async fn listen_to_transactions( database_trigger: &DatabaseTrigger, db: DB, - rsmq: Option, mut killpill_rx: tokio::sync::broadcast::Receiver<()>, ) -> Result<(), Error> { let resource = get_database_resource( @@ -354,7 +353,7 @@ async fn listen_to_transactions( )])); body.insert("trigger_info".to_string(), to_raw_value(&database_info)); let body = HashMap::from([("data".to_string(), to_raw_value(&serde_json::json!(body)))]); - let _ = run_job(Some(body), extra, &db, rsmq.clone(), database_trigger).await; + let _ = run_job(Some(body), extra, &db, database_trigger).await; continue; } @@ -370,7 +369,6 @@ async fn listen_to_transactions( async fn try_to_listen_to_database_transactions( db_trigger: DatabaseTrigger, db: DB, - rsmq: Option, killpill_rx: tokio::sync::broadcast::Receiver<()>, ) { let database_trigger = sqlx::query_scalar!( @@ -401,8 +399,7 @@ async fn try_to_listen_to_database_transactions( if has_lock.flatten().unwrap_or(false) { tracing::info!("Spawning new task to listen_to_database_transaction"); tokio::spawn(async move { - let result = - listen_to_transactions(&db_trigger, db.clone(), rsmq, killpill_rx).await; + let result = listen_to_transactions(&db_trigger, db.clone(), killpill_rx).await; if let Err(e) = result { update_ping(&db, &db_trigger, Some(e.to_string().as_str())).await; }; @@ -423,7 +420,6 @@ async fn try_to_listen_to_database_transactions( async fn listen_to_unlistened_database_events( db: &DB, - rsmq: &Option, killpill_rx: &tokio::sync::broadcast::Receiver<()>, ) { let database_triggers = sqlx::query_as!( @@ -466,7 +462,6 @@ async fn listen_to_unlistened_database_events( try_to_listen_to_database_transactions( trigger, db.clone(), - rsmq.clone(), killpill_rx.resubscribe(), ) .await; @@ -478,13 +473,9 @@ async fn listen_to_unlistened_database_events( }; } -pub async fn start_database( - db: DB, - rsmq: Option, - mut killpill_rx: tokio::sync::broadcast::Receiver<()>, -) { +pub async fn start_database(db: DB, mut killpill_rx: tokio::sync::broadcast::Receiver<()>) { tokio::spawn(async move { - listen_to_unlistened_database_events(&db, &rsmq, &killpill_rx).await; + listen_to_unlistened_database_events(&db, &killpill_rx).await; loop { tokio::select! { biased; @@ -492,7 +483,7 @@ pub async fn start_database( return; } _ = tokio::time::sleep(tokio::time::Duration::from_secs(15)) => { - listen_to_unlistened_database_events(&db, &rsmq, &killpill_rx).await + listen_to_unlistened_database_events(&db, &killpill_rx).await } } } diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index eb30c494dae4a..98b5d509a92af 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -59,6 +59,7 @@ mod auth; mod capture; mod concurrency_groups; mod configs; +mod database_triggers; mod db; mod drafts; pub mod ee; @@ -73,7 +74,6 @@ mod http_triggers; mod indexer_ee; mod inputs; mod integration; -mod database_triggers; #[cfg(feature = "enterprise")] mod apps_ee; @@ -94,6 +94,7 @@ mod scim_ee; mod scripts; mod service_logs; mod settings; +mod slack_approvals; #[cfg(feature = "smtp")] mod smtp_server_ee; mod static_assets; @@ -110,7 +111,6 @@ mod websocket_triggers; mod workers; mod workspaces; mod workspaces_ee; -mod slack_approvals; mod workspaces_export; mod workspaces_extra; @@ -290,10 +290,8 @@ pub async fn run_server( let kafka_killpill_rx = rx.resubscribe(); kafka_triggers_ee::start_kafka_consumers(db.clone(), kafka_killpill_rx).await; } - let ws_killpill_rx = rx.resubscribe(); - websocket_triggers::start_websockets(db.clone(), rsmq.clone(), ws_killpill_rx).await; let db_killpill_rx = rx.resubscribe(); - database_triggers::start_database(db.clone(), rsmq, db_killpill_rx).await; + database_triggers::start_database(db.clone(), db_killpill_rx).await; } // build our application with a route @@ -362,12 +360,11 @@ pub async fn run_server( #[cfg(not(feature = "websocket"))] Router::new() }) - .nest("/kafka_triggers", kafka_triggers_service), - .nest("/http_triggers", http_triggers::workspaced_service()) + .nest("/kafka_triggers", kafka_triggers_service) .nest( - "/websocket_triggers", - websocket_triggers::workspaced_service(), - ).nest("/database_triggers", database_triggers::workspaced_service()), + "/database_triggers", + database_triggers::workspaced_service(), + ), ) .nest("/workspaces", workspaces::global_service()) .nest( @@ -427,7 +424,10 @@ pub async fn run_server( jobs::workspace_unauthed_service().layer(cors.clone()), ) .route("/slack", post(slack_approvals::slack_app_callback_handler)) - .route("/w/:workspace_id/jobs/slack_approval/:job_id", get(slack_approvals::request_slack_approval)) + .route( + "/w/:workspace_id/jobs/slack_approval/:job_id", + get(slack_approvals::request_slack_approval), + ) .nest( "/w/:workspace_id/resources_u", resources::public_service().layer(cors.clone()), diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index cb42b813e1ed4..ba95eb8f6c853 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -35,7 +35,7 @@ use windmill_audit::ActionKind; use windmill_common::db::UserDB; use windmill_common::s3_helpers::LargeFileStorage; use windmill_common::users::username_to_permissioned_as; -use windmill_common::variables::build_crypt; +use windmill_common::variables::{build_crypt, decrypt, encrypt}; use windmill_common::worker::to_raw_value; #[cfg(feature = "enterprise")] use windmill_common::workspaces::WorkspaceDeploymentUISettings; @@ -52,7 +52,6 @@ use windmill_git_sync::handle_deployment_metadata; #[cfg(feature = "enterprise")] use windmill_common::utils::require_admin_or_devops; -use crate::variables::{decrypt, encrypt}; use hyper::StatusCode; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Postgres, Transaction}; @@ -121,14 +120,6 @@ pub fn workspaced_service() -> Router { "/critical_alerts/acknowledge_all", post(acknowledge_all_critical_alerts), ) - .route( - "/critical_alerts/:id/acknowledge", - post(acknowledge_critical_alert), - ) - .route( - "/critical_alerts/acknowledge_all", - post(acknowledge_all_critical_alerts), - ) .route("/critical_alerts/mute", post(mute_critical_alerts)); #[cfg(feature = "stripe")] @@ -2091,10 +2082,6 @@ async fn mute_critical_alerts( "Updated mute criticital alert ui settings for workspace: {}", &w_id )) - Ok(format!( - "Updated mute criticital alert ui settings for workspace: {}", - &w_id - )) } #[cfg(not(feature = "enterprise"))] diff --git a/backend/windmill-api/src/workspaces_export.rs b/backend/windmill-api/src/workspaces_export.rs index 774bc97053e56..8c0cd1c85aefb 100644 --- a/backend/windmill-api/src/workspaces_export.rs +++ b/backend/windmill-api/src/workspaces_export.rs @@ -25,18 +25,15 @@ use axum::{ use http::HeaderName; use itertools::Itertools; -use windmill_common::db::UserDB; -use windmill_common::schedule::Schedule; -use windmill_common::variables::build_crypt; - use windmill_common::{ + db::UserDB, error::{to_anyhow, Error, Result}, flows::Flow, + schedule::Schedule, scripts::{Schema, Script, ScriptLang}, - variables::ExportableListableVariable, + variables::{build_crypt, decrypt, ExportableListableVariable}, }; -use crate::variables::decrypt; use hyper::header; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index e1587cd7e2126..1e9f7b5d913f6 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -248,18 +248,16 @@ pub async fn connect_db( Err(_) => { if server_mode { DEFAULT_MAX_CONNECTIONS_SERVER + } else if indexer_mode { + DEFAULT_MAX_CONNECTIONS_INDEXER } else { - if indexer_mode { - DEFAULT_MAX_CONNECTIONS_INDEXER - } else { - DEFAULT_MAX_CONNECTIONS_WORKER - + std::env::var("NUM_WORKERS") - .ok() - .map(|x| x.parse().ok()) - .flatten() - .unwrap_or(1) - - 1 - } + DEFAULT_MAX_CONNECTIONS_WORKER + + std::env::var("NUM_WORKERS") + .ok() + .map(|x| x.parse().ok()) + .flatten() + .unwrap_or(1) + - 1 } } }; diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 09135cfa42407..7b39b0ca00a43 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -95,34 +95,7 @@ impl TryFrom<&str> for ScriptLang { "php" => Self::Php, "rust" => Self::Rust, "ansible" => Self::Ansible, - _ => return Err("Language not supported".to_string()), - }; - - Ok(language) - } -} - -impl TryFrom<&str> for ScriptLang { - type Error = String; - fn try_from(value: &str) -> Result { - let language = match value { - "bun" => Self::Bun, - "bunnative" => Self::Bunnative, - "nativets" => Self::Nativets, - "deno" => Self::Deno, - "python3" => Self::Python3, - "go" => Self::Go, - "bash" => Self::Bash, - "powershell" => Self::Powershell, - "postgresql" => Self::Postgresql, - "mysql" => Self::Mysql, - "bigquery" => Self::Bigquery, - "snowflake" => Self::Snowflake, - "mssql" => Self::Mssql, - "graphql" => Self::Graphql, - "php" => Self::Php, - "rust" => Self::Rust, - "ansible" => Self::Ansible, + "csharp" => Self::CSharp, _ => return Err("Language not supported".to_string()), }; diff --git a/frontend/openapi-ts-error-1732381784283.log b/frontend/openapi-ts-error-1732381784283.log deleted file mode 100644 index 80a69e4c8faaf..0000000000000 --- a/frontend/openapi-ts-error-1732381784283.log +++ /dev/null @@ -1,28 +0,0 @@ -Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (12261:18) - - 12258 | type: string - 12259 | columns_name: - 12260 | type: array - 12261 | items: ---------------------------^ - 12262 | type: string - 12263 | required: -JSONParserError: Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (12261:18) - - 12258 | type: string - 12259 | columns_name: - 12260 | type: array - 12261 | items: ---------------------------^ - 12262 | type: string - 12263 | required: - at Object.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23) - at getResult (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22) - at runNextPlugin (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32) - at /home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9 - at new Promise () - at Object.run (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12) - at parseFile (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38) - at parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30) - at async $RefParser.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28) - at async $RefParser.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1732447138336.log b/frontend/openapi-ts-error-1732447138336.log deleted file mode 100644 index 9e7156b4c2df0..0000000000000 --- a/frontend/openapi-ts-error-1732447138336.log +++ /dev/null @@ -1,12 +0,0 @@ -Token "DatabaseToTrackWithoutPass" does not exist. -JSONParserError: Token "DatabaseToTrackWithoutPass" does not exist. - at Pointer.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:103:23) - at $Ref.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28) - at $Refs._resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:158:21) - at inventory$Ref (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:116:27) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:91:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at inventory$Ref (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:153:9) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:91:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1733590385427.log b/frontend/openapi-ts-error-1733590385427.log deleted file mode 100644 index 4f1877c4cbdc1..0000000000000 --- a/frontend/openapi-ts-error-1733590385427.log +++ /dev/null @@ -1,5 +0,0 @@ -Error opening file "/home/dieri/windmill/backend/windmill-api/TransactionType" -ENOENT: no such file or directory, open '/home/dieri/windmill/backend/windmill-api/TransactionType' -JSONParserError: Error opening file "/home/dieri/windmill/backend/windmill-api/TransactionType" -ENOENT: no such file or directory, open '/home/dieri/windmill/backend/windmill-api/TransactionType' - at Object.read (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/resolvers/file.js:61:19) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734292309527.log b/frontend/openapi-ts-error-1734292309527.log deleted file mode 100644 index a6d8706d21fd1..0000000000000 --- a/frontend/openapi-ts-error-1734292309527.log +++ /dev/null @@ -1,28 +0,0 @@ -Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (12315:18) - - 12312 | type: string - 12313 | relations: - 12314 | type: array - 12315 | items: ---------------------------^ - 12316 | $ref: "#/components/schemas/Relat ... - 12317 | language: -JSONParserError: Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (12315:18) - - 12312 | type: string - 12313 | relations: - 12314 | type: array - 12315 | items: ---------------------------^ - 12316 | $ref: "#/components/schemas/Relat ... - 12317 | language: - at Object.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23) - at getResult (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22) - at runNextPlugin (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32) - at /home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9 - at new Promise () - at Object.run (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12) - at parseFile (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38) - at parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30) - at async $RefParser.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28) - at async $RefParser.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734309008594.log b/frontend/openapi-ts-error-1734309008594.log deleted file mode 100644 index c2a2d4422dd3e..0000000000000 --- a/frontend/openapi-ts-error-1734309008594.log +++ /dev/null @@ -1,12 +0,0 @@ -Cannot read properties of undefined (reading 'replace') -TypeError: Cannot read properties of undefined (reading 'replace') - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:16370 - at Me (/home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:16406) - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:30182 - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:31472 - at Array.forEach () - at nt (/home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:29879) - at it (/home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:34666) - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:38377 - at Array.forEach () - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:38356 \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734309033613.log b/frontend/openapi-ts-error-1734309033613.log deleted file mode 100644 index c2a2d4422dd3e..0000000000000 --- a/frontend/openapi-ts-error-1734309033613.log +++ /dev/null @@ -1,12 +0,0 @@ -Cannot read properties of undefined (reading 'replace') -TypeError: Cannot read properties of undefined (reading 'replace') - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:16370 - at Me (/home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:16406) - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:30182 - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:31472 - at Array.forEach () - at nt (/home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:29879) - at it (/home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:34666) - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:38377 - at Array.forEach () - at /home/dieri/windmill/frontend/node_modules/@hey-api/openapi-ts/dist/node/index.cjs:1:38356 \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734741871024.log b/frontend/openapi-ts-error-1734741871024.log deleted file mode 100644 index 24ed6b91d5e11..0000000000000 --- a/frontend/openapi-ts-error-1734741871024.log +++ /dev/null @@ -1,28 +0,0 @@ -Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (7736:20) - - 7733 | responses: - 7734 | "201": - 7735 | description: publication created - 7736 | content: ----------------------------^ - 7737 | text/plain: - 7738 | schema: -JSONParserError: Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (7736:20) - - 7733 | responses: - 7734 | "201": - 7735 | description: publication created - 7736 | content: ----------------------------^ - 7737 | text/plain: - 7738 | schema: - at Object.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23) - at getResult (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22) - at runNextPlugin (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32) - at /home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9 - at new Promise () - at Object.run (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12) - at parseFile (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38) - at parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30) - at async $RefParser.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28) - at async $RefParser.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734741998365.log b/frontend/openapi-ts-error-1734741998365.log deleted file mode 100644 index 85564887fed08..0000000000000 --- a/frontend/openapi-ts-error-1734741998365.log +++ /dev/null @@ -1,28 +0,0 @@ -Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (7761:20) - - 7758 | responses: - 7759 | "201": - 7760 | description: publication updated - 7761 | content: ----------------------------^ - 7762 | text/plain: - 7763 | schema: -JSONParserError: Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: bad indentation of a mapping entry (7761:20) - - 7758 | responses: - 7759 | "201": - 7760 | description: publication updated - 7761 | content: ----------------------------^ - 7762 | text/plain: - 7763 | schema: - at Object.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23) - at getResult (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22) - at runNextPlugin (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32) - at /home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9 - at new Promise () - at Object.run (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12) - at parseFile (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38) - at parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30) - at async $RefParser.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28) - at async $RefParser.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734742008826.log b/frontend/openapi-ts-error-1734742008826.log deleted file mode 100644 index 7e74fbc33a16a..0000000000000 --- a/frontend/openapi-ts-error-1734742008826.log +++ /dev/null @@ -1,12 +0,0 @@ -Token "DatabaseResourcePath" does not exist. -JSONParserError: Token "DatabaseResourcePath" does not exist. - at Pointer.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:103:23) - at $Ref.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28) - at $Refs._resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:158:21) - at inventory$Ref (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:116:27) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:91:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at bundle (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:44:5) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734793209662.log b/frontend/openapi-ts-error-1734793209662.log deleted file mode 100644 index cae377b6a185b..0000000000000 --- a/frontend/openapi-ts-error-1734793209662.log +++ /dev/null @@ -1,28 +0,0 @@ -Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (7735:3) - - 7732 | schema: - 7733 | $ref: "#/compone ... - 7734 | - 7735 | /w/{workspace}/database_trigge ... -----------^ - 7736 | get: - 7737 | summary: get database publ ... -JSONParserError: Error parsing /home/dieri/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (7735:3) - - 7732 | schema: - 7733 | $ref: "#/compone ... - 7734 | - 7735 | /w/{workspace}/database_trigge ... -----------^ - 7736 | get: - 7737 | summary: get database publ ... - at Object.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23) - at getResult (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22) - at runNextPlugin (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32) - at /home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9 - at new Promise () - at Object.run (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12) - at parseFile (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38) - at parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30) - at async $RefParser.parse (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28) - at async $RefParser.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13) \ No newline at end of file diff --git a/frontend/openapi-ts-error-1734795515701.log b/frontend/openapi-ts-error-1734795515701.log deleted file mode 100644 index 7e74fbc33a16a..0000000000000 --- a/frontend/openapi-ts-error-1734795515701.log +++ /dev/null @@ -1,12 +0,0 @@ -Token "DatabaseResourcePath" does not exist. -JSONParserError: Token "DatabaseResourcePath" does not exist. - at Pointer.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:103:23) - at $Ref.resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28) - at $Refs._resolve (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:158:21) - at inventory$Ref (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:116:27) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:91:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at crawl (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:94:21) - at bundle (/home/dieri/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/bundle.js:44:5) \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2cea8be8104ae..5cba83f880e7f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -38,6 +38,7 @@ "chartjs-plugin-zoom": "^2.0.0", "d3-zoom": "^3.0.0", "date-fns": "^2.30.0", + "dayjs": "^1.11.13", "diff": "^5.1.0", "driver.js": "^1.3.0", "esm-env": "^1.0.0", @@ -69,6 +70,7 @@ "vscode-languageclient": "~9.0.1", "vscode-uri": "~3.0.8", "vscode-ws-jsonrpc": "~3.3.2", + "windmill-client": "^1.441.1", "windmill-parser-wasm-csharp": "^1.437.1", "windmill-parser-wasm-go": "^1.429.0", "windmill-parser-wasm-php": "^1.429.0", @@ -6191,6 +6193,12 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debounce-promise": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", @@ -13650,6 +13658,12 @@ "node": ">= 8" } }, + "node_modules/windmill-client": { + "version": "1.441.1", + "resolved": "https://registry.npmjs.org/windmill-client/-/windmill-client-1.441.1.tgz", + "integrity": "sha512-GxEtG4wFdR9TDRek5T58jLKNIxfNTM8YTdGOnrfs/oYAi/TlvOPfD7dfqr8w8/7RMtIu0UI8zsSB2RndPPjjkw==", + "license": "Apache 2.0" + }, "node_modules/windmill-parser-wasm-csharp": { "version": "1.437.1", "resolved": "https://registry.npmjs.org/windmill-parser-wasm-csharp/-/windmill-parser-wasm-csharp-1.437.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index ed31ea8660584..9a34018247c27 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -111,6 +111,7 @@ "chartjs-plugin-zoom": "^2.0.0", "d3-zoom": "^3.0.0", "date-fns": "^2.30.0", + "dayjs": "^1.11.13", "diff": "^5.1.0", "driver.js": "^1.3.0", "esm-env": "^1.0.0", @@ -142,6 +143,7 @@ "vscode-languageclient": "~9.0.1", "vscode-uri": "~3.0.8", "vscode-ws-jsonrpc": "~3.3.2", + "windmill-client": "^1.441.1", "windmill-parser-wasm-csharp": "^1.437.1", "windmill-parser-wasm-go": "^1.429.0", "windmill-parser-wasm-php": "^1.429.0", diff --git a/frontend/src/lib/components/AuthSettings.svelte b/frontend/src/lib/components/AuthSettings.svelte index 827f5cd770668..59a1df7c7704c 100644 --- a/frontend/src/lib/components/AuthSettings.svelte +++ b/frontend/src/lib/components/AuthSettings.svelte @@ -1,4 +1,4 @@ - { let newHash = e.detail - destroyDatabaseTrigger = false goto(`/scripts/get/${newHash}?workspace=${$workspaceStore}`) }} on:saveInitial={(e) => { From 7011cda7e7dc3ead4c04e23a870e9b432aa0e1f9 Mon Sep 17 00:00:00 2001 From: dieriba Date: Wed, 25 Dec 2024 14:36:06 +0100 Subject: [PATCH 38/50] feat: --- frontend/package-lock.json | 14 ------------ frontend/package.json | 2 -- frontend/src/lib/components/Login.svelte | 22 ++++++++++++------- .../components/triggers/TriggersEditor.svelte | 12 +++++----- .../ConfigurationEditor.svelte | 3 +++ .../DatabaseTriggerEditor.svelte | 0 .../DatabaseTriggerEditorInner.svelte | 0 .../DatabaseTriggersPanel.svelte | 0 .../PublicationPicker.svelte | 0 .../SlotPicker.svelte | 0 .../RouteEditor.svelte | 0 .../RouteEditorInner.svelte | 0 .../RoutesPanel.svelte | 0 .../{ => kafka}/KafkaTriggerEditor.svelte | 0 .../KafkaTriggerEditorInner.svelte | 4 ++-- .../{ => kafka}/KafkaTriggersPanel.svelte | 8 +++---- .../ScheduledPollPanel.svelte | 0 .../WebhooksPanel.svelte | 0 .../WebsocketTriggerEditor.svelte | 0 .../WebsocketTriggerEditorInner.svelte | 0 .../WebsocketTriggersPanel.svelte | 0 .../(logged)/database_triggers/+page.svelte | 2 +- .../(logged)/flows/get/[...path]/+page.svelte | 10 ++++----- .../(logged)/kafka_triggers/+page.svelte | 2 +- .../(root)/(logged)/routes/+page.svelte | 2 +- .../scripts/get/[...hash]/+page.svelte | 10 ++++----- .../(logged)/websocket_triggers/+page.svelte | 2 +- package-lock.json | 6 +++++ package.json | 1 + 29 files changed, 49 insertions(+), 51 deletions(-) rename frontend/src/lib/components/triggers/{database_triggers => database}/ConfigurationEditor.svelte (99%) rename frontend/src/lib/components/triggers/{database_triggers => database}/DatabaseTriggerEditor.svelte (100%) rename frontend/src/lib/components/triggers/{database_triggers => database}/DatabaseTriggerEditorInner.svelte (100%) rename frontend/src/lib/components/triggers/{database_triggers => database}/DatabaseTriggersPanel.svelte (100%) rename frontend/src/lib/components/triggers/{database_triggers => database}/PublicationPicker.svelte (100%) rename frontend/src/lib/components/triggers/{database_triggers => database}/SlotPicker.svelte (100%) rename frontend/src/lib/components/triggers/{http_triggers => http}/RouteEditor.svelte (100%) rename frontend/src/lib/components/triggers/{http_triggers => http}/RouteEditorInner.svelte (100%) rename frontend/src/lib/components/triggers/{http_triggers => http}/RoutesPanel.svelte (100%) rename frontend/src/lib/components/triggers/{ => kafka}/KafkaTriggerEditor.svelte (100%) rename frontend/src/lib/components/triggers/{ => kafka}/KafkaTriggerEditorInner.svelte (98%) rename frontend/src/lib/components/triggers/{ => kafka}/KafkaTriggersPanel.svelte (92%) rename frontend/src/lib/components/triggers/{scheduled_triggers => scheduled}/ScheduledPollPanel.svelte (100%) rename frontend/src/lib/components/triggers/{webhook_triggers => webhook}/WebhooksPanel.svelte (100%) rename frontend/src/lib/components/triggers/{websocket_triggers => websocket}/WebsocketTriggerEditor.svelte (100%) rename frontend/src/lib/components/triggers/{websocket_triggers => websocket}/WebsocketTriggerEditorInner.svelte (100%) rename frontend/src/lib/components/triggers/{websocket_triggers => websocket}/WebsocketTriggersPanel.svelte (100%) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5cba83f880e7f..2cea8be8104ae 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -38,7 +38,6 @@ "chartjs-plugin-zoom": "^2.0.0", "d3-zoom": "^3.0.0", "date-fns": "^2.30.0", - "dayjs": "^1.11.13", "diff": "^5.1.0", "driver.js": "^1.3.0", "esm-env": "^1.0.0", @@ -70,7 +69,6 @@ "vscode-languageclient": "~9.0.1", "vscode-uri": "~3.0.8", "vscode-ws-jsonrpc": "~3.3.2", - "windmill-client": "^1.441.1", "windmill-parser-wasm-csharp": "^1.437.1", "windmill-parser-wasm-go": "^1.429.0", "windmill-parser-wasm-php": "^1.429.0", @@ -6193,12 +6191,6 @@ "url": "https://opencollective.com/date-fns" } }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" - }, "node_modules/debounce-promise": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", @@ -13658,12 +13650,6 @@ "node": ">= 8" } }, - "node_modules/windmill-client": { - "version": "1.441.1", - "resolved": "https://registry.npmjs.org/windmill-client/-/windmill-client-1.441.1.tgz", - "integrity": "sha512-GxEtG4wFdR9TDRek5T58jLKNIxfNTM8YTdGOnrfs/oYAi/TlvOPfD7dfqr8w8/7RMtIu0UI8zsSB2RndPPjjkw==", - "license": "Apache 2.0" - }, "node_modules/windmill-parser-wasm-csharp": { "version": "1.437.1", "resolved": "https://registry.npmjs.org/windmill-parser-wasm-csharp/-/windmill-parser-wasm-csharp-1.437.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9a34018247c27..ed31ea8660584 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -111,7 +111,6 @@ "chartjs-plugin-zoom": "^2.0.0", "d3-zoom": "^3.0.0", "date-fns": "^2.30.0", - "dayjs": "^1.11.13", "diff": "^5.1.0", "driver.js": "^1.3.0", "esm-env": "^1.0.0", @@ -143,7 +142,6 @@ "vscode-languageclient": "~9.0.1", "vscode-uri": "~3.0.8", "vscode-ws-jsonrpc": "~3.3.2", - "windmill-client": "^1.441.1", "windmill-parser-wasm-csharp": "^1.437.1", "windmill-parser-wasm-go": "^1.429.0", "windmill-parser-wasm-php": "^1.429.0", diff --git a/frontend/src/lib/components/Login.svelte b/frontend/src/lib/components/Login.svelte index f09d68d37652f..396cc2d4696be 100644 --- a/frontend/src/lib/components/Login.svelte +++ b/frontend/src/lib/components/Login.svelte @@ -148,14 +148,20 @@ } async function loadLogins() { - const allLogins = await OauthService.listOauthLogins() - logins = allLogins.oauth.map((login) => ({ - type: login.type, - displayName: login.display_name || login.type - })) - saml = allLogins.saml - - showPassword = (logins.length == 0 && !saml) || (email != undefined && email.length > 0) + try { + const allLogins = await OauthService.listOauthLogins() + logins = allLogins.oauth.map((login) => ({ + type: login.type, + displayName: login.display_name || login.type + })) + saml = allLogins.saml + showPassword = (logins.length == 0 && !saml) || (email != undefined && email.length > 0) + } catch (e) { + logins = [] + saml = undefined + showPassword = true + console.error('Could not load logins', e) + } } loadLogins() diff --git a/frontend/src/lib/components/triggers/TriggersEditor.svelte b/frontend/src/lib/components/triggers/TriggersEditor.svelte index 1cafecad21ff2..142bb4f4f298f 100644 --- a/frontend/src/lib/components/triggers/TriggersEditor.svelte +++ b/frontend/src/lib/components/triggers/TriggersEditor.svelte @@ -1,19 +1,19 @@