Merge branch 'main' into fr/flow-triggers-v0

This commit is contained in:
Ruben Fiszel
2024-10-10 03:53:09 +02:00
51 changed files with 1211 additions and 540 deletions
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## [1.406.0](https://github.com/windmill-labs/windmill/compare/v1.405.5...v1.406.0) (2024-10-09)
### Features
* **frontend:** components can be moved inside containers by holding ctrl/cmd ([111bfc6](https://github.com/windmill-labs/windmill/commit/111bfc6a659037ae7029e8f557256e2fffcf979b))
* **monitoring:** Critical Alerts for Jobs Waiting in Queue [enterprise] ([#4491](https://github.com/windmill-labs/windmill/issues/4491)) ([d90d6c2](https://github.com/windmill-labs/windmill/commit/d90d6c2b896c5f99e00681656f376b180901f272))
### Bug Fixes
* **cli:** instance sync push does not require sync pull ([257f097](https://github.com/windmill-labs/windmill/commit/257f0971f86938da71b879f32d93473976eaa920))
* remove monaco-editor for app preview code path for faster app loads ([7b05033](https://github.com/windmill-labs/windmill/commit/7b0503332d1bdd7f5999a5ef99150f8c9f6f18be))
## [1.405.5](https://github.com/windmill-labs/windmill/compare/v1.405.4...v1.405.5) (2024-10-04)
+1 -1
View File
@@ -175,7 +175,7 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
COPY --from=builder /frontend/build /static_frontend
COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
COPY --from=denoland/deno:1.46.3 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=denoland/deno:2.0.0 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM healthchecks WHERE check_type = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a"
}
@@ -0,0 +1,12 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM healthchecks WHERE healthy = true AND created_at < NOW() - INTERVAL '14 days'",
"describe": {
"columns": [],
"parameters": {
"Left": []
},
"nullable": []
},
"hash": "2041526bc58872d71f91f7698144039bd67f8e37895befa94a15b7e4019e114b"
}
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO healthchecks (check_type, healthy) VALUES ($1, false)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "27920aaa55666ffc14a36a247f89ff7994ee40d3953b9f772d0e0ab999bccb7b"
}
@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT created_at FROM healthchecks WHERE check_type = $1 ORDER BY created_at DESC LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "created_at",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "34a45763bb4d14162f4cd3fa07cd8020f1f6085f4ee85f5eab3458637edf26cd"
}
@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT COUNT(*) as count, \n MIN(scheduled_for) as oldest_job\n FROM queue \n WHERE tag = $1 \n AND scheduled_for <= NOW() - $2::interval \n AND running = false\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "oldest_job",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Text",
"Interval"
]
},
"nullable": [
null,
null
]
},
"hash": "3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30"
}
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO concurrency_locks (id, last_locked_at) VALUES ($1, NOW()) ON CONFLICT (id) DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "900ac59515e4283f4b57516210575dfe92f74a7220ed69e61899a6e0f053d9cd"
}
@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE healthchecks SET healthy = true WHERE check_type = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "ad42118ccf6a9d2d1e072c4df064ddf964a5b3cd088fc162d0d8222325d4a5ea"
}
@@ -18,8 +18,8 @@
"Left": []
},
"nullable": [
true,
false
false,
true
]
},
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
@@ -52,7 +52,8 @@
"trigger",
"failure",
"command",
"approval"
"approval",
"preprocessor"
]
}
}
@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE concurrency_locks SET last_locked_at = NOW() WHERE id = $1 AND last_locked_at < NOW() - INTERVAL '1 second' * $2 RETURNING 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Float8"
]
},
"nullable": [
null
]
},
"hash": "c4e1873bfc7b905e7299a021f4baa2a97e95f4797c5e11f37822e19828422b7e"
}
@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT 1 FROM healthchecks WHERE check_type = $1 AND healthy = false)",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
null
]
},
"hash": "eb932b613a6dbb2cdff97e5512d42b538ba83115c0ea798be00b01659600f45a"
}
+295 -292
View File
File diff suppressed because it is too large Load Diff
+20 -17
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.405.5"
version = "1.406.0"
authors.workspace = true
edition.workspace = true
@@ -27,7 +27,7 @@ members = [
]
[workspace.package]
version = "1.405.5"
version = "1.406.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -172,20 +172,24 @@ tokio-util = { version = "^0", features = ["io"] }
json-pointer = "^0"
itertools = "^0"
regex = "^1"
deno_fetch = "0.187.0"
deno_tls = "0.150.0"
deno_console = "0.163.0"
deno_url = "0.163.0"
deno_webidl = "0.163.0"
deno_web = "0.194.0"
deno_net = "0.155.0"
deno_core = "0.299.0"
deno_ast = { version = "=0.40.0", features = ["transpiling"] }
deno_fetch = "0.195.0"
deno_tls = "0.158.0"
deno_console = "0.171.0"
deno_url = "0.171.0"
deno_webidl = "0.171.0"
deno_web = "0.202.0"
deno_net = "0.163.0"
deno_core = "0.311.0"
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
swc_common = "=0.37.5"
swc_ecma_parser = "=0.149.1"
swc_ecma_ast = "=0.118.2"
swc_ecma_visit = "=0.104.8"
async-recursion = "^1"
swc_common = "=0.33.26"
swc_ecma_parser = "=0.144.3"
swc_ecma_ast = "=0.113.7"
swc_ecma_visit = "=0.99.1"
base64 = "0.21.0"
base32 = "^0"
hmac = "0.12.1"
@@ -270,8 +274,7 @@ tikv-jemallocator = { version = "0.5" }
tikv-jemalloc-sys = { version = "^0.5" }
tikv-jemalloc-ctl = { version = "^0.5" }
# 0.1.12 broken (nested dependency of swc_common)
triomphe = "<0.1.12"
triomphe = "^0"
tantivy = "0.22.0"
+1 -1
View File
@@ -1 +1 @@
0f5f42d2f8f5f1af05c8086f3dc7ad38d83750df
8e3fbda05392e641c15262924dc0df47cc42d036
@@ -0,0 +1,2 @@
-- Drop the alert_locks table
DROP TABLE IF EXISTS concurrency_locks;
@@ -0,0 +1,6 @@
-- Create the alert_locks table
CREATE TABLE concurrency_locks (
id VARCHAR PRIMARY KEY,
last_locked_at TIMESTAMP NOT NULL,
owner VARCHAR NULL
);
+15 -15
View File
@@ -15,13 +15,13 @@ use windmill_parser::{
use swc_common::{sync::Lrc, FileName, SourceMap, SourceMapper, Span, Spanned};
use swc_ecma_ast::{
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident, Lit,
MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat, Param, Pat, Str,
TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsOptionalType,
TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement, TsTypeLit,
TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident,
IdentName, Lit, MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat,
Param, Pat, Str, TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType,
TsOptionalType, TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement,
TsTypeLit, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
};
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_parser::{lexer::Lexer, EsSyntax, Parser, StringInput, Syntax, TsSyntax};
use regex::Regex;
#[cfg(target_arch = "wasm32")]
@@ -48,9 +48,9 @@ impl Visit for ImportsFinder {
pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()), code.into());
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()).into(), code.into());
let lexer = Lexer::new(
Syntax::Typescript(TsConfig::default()),
Syntax::Typescript(TsSyntax::default()),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),
@@ -69,7 +69,7 @@ pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
})?;
let mut visitor = ImportsFinder { imports: HashSet::new() };
swc_ecma_visit::visit_module(&mut visitor, &expr);
visitor.visit_module(&expr);
Ok(visitor.imports.into_iter().collect())
}
@@ -87,7 +87,7 @@ impl Visit for OutputFinder {
c.visit_with(self);
}
match m {
MemberExpr { obj, prop: MemberProp::Ident(Ident { sym, .. }), .. } => {
MemberExpr { obj, prop: MemberProp::Ident(IdentName { sym, .. }), .. } => {
match *obj.to_owned() {
Expr::Ident(Ident { sym: sym_i, .. }) => {
self.idents.insert((sym_i.to_string(), sym.to_string()));
@@ -102,10 +102,10 @@ impl Visit for OutputFinder {
pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.ts".into()), code.into());
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
let lexer = Lexer::new(
// We want to parse ecmascript
Syntax::Es(EsConfig { jsx: false, ..Default::default() }),
Syntax::Es(EsSyntax { jsx: false, ..Default::default() }),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),
@@ -124,7 +124,7 @@ pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
})?;
let mut visitor = OutputFinder { idents: HashSet::new() };
swc_ecma_visit::visit_module(&mut visitor, &expr);
visitor.visit_module(&expr);
Ok(visitor.idents.into_iter().collect())
}
@@ -135,10 +135,10 @@ pub fn parse_deno_signature(
main_override: Option<String>,
) -> anyhow::Result<MainArgSignature> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.ts".into()), code.into());
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
let lexer = Lexer::new(
// We want to parse ecmascript
Syntax::Typescript(TsConfig::default()),
Syntax::Typescript(TsSyntax::default()),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),
+1 -1
View File
@@ -116,7 +116,7 @@ where
pub fn main() -> anyhow::Result<()> {
#[cfg(feature = "deno_core")]
deno_core::JsRuntime::init_platform(None);
deno_core::JsRuntime::init_platform(None, false);
create_and_run_current_thread_inner(windmill_main())
}
+10 -2
View File
@@ -27,7 +27,7 @@ use windmill_api::{
DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN,
};
#[cfg(feature = "enterprise")]
use windmill_common::ee::worker_groups_alerts;
use windmill_common::ee::{worker_groups_alerts, jobs_waiting_alerts};
use windmill_common::{
auth::JWT_SECRET,
ee::CriticalErrorChannel,
@@ -1061,12 +1061,20 @@ pub async fn monitor_db(
}
};
let jobs_waiting_alerts_f = async {
#[cfg(feature = "enterprise")]
if server_mode {
jobs_waiting_alerts(&db).await;
}
};
join!(
expired_items_f,
zombie_jobs_f,
expose_queue_metrics_f,
verify_license_key_f,
worker_groups_alerts_f
worker_groups_alerts_f,
jobs_waiting_alerts_f,
);
}
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.405.5
version: 1.406.0
title: Windmill API
contact:
+3
View File
@@ -81,3 +81,6 @@ pub async fn create_customer_portal_session(
#[cfg(feature = "enterprise")]
pub async fn worker_groups_alerts(_db: &DB) {}
#[cfg(feature = "enterprise")]
pub async fn jobs_waiting_alerts(_db: &DB) {}
+26 -16
View File
@@ -5,15 +5,17 @@ use deno_net::NetPermissions;
#[cfg(feature = "deno_core")]
use deno_web::{BlobStore, TimersPermission};
#[cfg(feature = "deno_core")]
use std::borrow::Cow;
#[cfg(feature = "deno_core")]
use std::env;
#[cfg(feature = "deno_core")]
use std::io::Write;
#[cfg(feature = "deno_core")]
use std::path::PathBuf;
use std::path::{Path, PathBuf};
#[cfg(feature = "deno_core")]
use std::sync::Arc;
#[cfg(feature = "deno_core")]
// #[cfg(feature = "deno_core")]
pub struct PermissionsContainer;
#[cfg(feature = "deno_core")]
@@ -24,16 +26,16 @@ impl FetchPermissions for PermissionsContainer {
_url: &deno_core::url::Url,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
unreachable!("snapshotting")
}
#[inline(always)]
fn check_read(
fn check_read<'a>(
&mut self,
_p: &std::path::Path,
_p: &'a std::path::Path,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
) -> Result<Cow<'a, Path>, deno_core::error::AnyError> {
unreachable!("snapshotting")
}
}
@@ -47,20 +49,20 @@ impl TimersPermission for PermissionsContainer {
#[cfg(feature = "deno_core")]
impl NetPermissions for PermissionsContainer {
fn check_read(
fn check_read<'a>(
&mut self,
_p: &std::path::Path,
_p: &'a str,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting")
}
fn check_write(
fn check_write<'a>(
&mut self,
_p: &std::path::Path,
_p: &'a str,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
) -> Result<PathBuf, deno_core::error::AnyError> {
unreachable!("snapshotting")
}
fn check_net<T: AsRef<str>>(
@@ -68,7 +70,15 @@ impl NetPermissions for PermissionsContainer {
_host: &(T, Option<u16>),
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
unreachable!("snapshotting")
}
fn check_write_path<'a>(
&mut self,
_: &'a Path,
_: &str,
) -> Result<Cow<'a, Path>, deno_core::anyhow::Error> {
todo!()
}
}
+22 -12
View File
@@ -8,9 +8,11 @@
#[cfg(feature = "deno_core")]
use std::{
borrow::Cow,
cell::RefCell,
env,
io::{self, BufReader},
path::PathBuf,
rc::Rc,
};
@@ -111,12 +113,12 @@ impl FetchPermissions for PermissionsContainer {
}
#[inline(always)]
fn check_read(
fn check_read<'a>(
&mut self,
_p: &std::path::Path,
p: &'a std::path::Path,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
) -> Result<Cow<'a, std::path::Path>, anyhow::Error> {
Ok(Cow::Borrowed(p))
}
}
@@ -130,20 +132,20 @@ impl TimersPermission for PermissionsContainer {
#[cfg(feature = "deno_core")]
impl NetPermissions for PermissionsContainer {
fn check_read(
fn check_read<'a>(
&mut self,
_p: &std::path::Path,
p: &'a str,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
) -> Result<PathBuf, deno_core::error::AnyError> {
Ok(PathBuf::from(p))
}
fn check_write(
fn check_write<'a>(
&mut self,
_p: &std::path::Path,
p: &'a str,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
Ok(())
) -> Result<PathBuf, deno_core::error::AnyError> {
Ok(PathBuf::from(p))
}
fn check_net<T: AsRef<str>>(
@@ -153,6 +155,14 @@ impl NetPermissions for PermissionsContainer {
) -> Result<(), deno_core::error::AnyError> {
Ok(())
}
fn check_write_path<'a>(
&mut self,
p: &'a std::path::Path,
_api_name: &str,
) -> Result<std::borrow::Cow<'a, std::path::Path>, AnyError> {
Ok(Cow::Borrowed(p))
}
}
#[cfg(feature = "deno_core")]
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.405.5";
export const VERSION = "v1.406.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+1 -1
View File
@@ -60,7 +60,7 @@ export {
// }
// });
export const VERSION = "1.405.5";
export const VERSION = "1.406.0";
const command = new Command()
.name("wmill")
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.405.5",
"version": "1.406.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.405.5",
"version": "1.406.0",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.405.5",
"version": "1.406.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -300,9 +300,9 @@
"types": "./package/utils.d.ts",
"default": "./package/utils.js"
},
"./icons/store": {
"types": "./package/icons/store.d.ts",
"default": "./package/icons/store.js"
"./components/icons/store": {
"types": "./package/components/icons/store.d.ts",
"default": "./package/components/icons/store.js"
},
"./script_helpers": {
"types": "./package/script_helpers.d.ts",
@@ -481,8 +481,8 @@
"script_helpers": [
"./package/script_helpers.d.ts"
],
"icons/store": [
"./package/icons/store.d.ts"
"components/icons/store": [
"./package/components/icons/store.d.ts"
]
}
},
@@ -1,6 +1,4 @@
<script lang="ts">
import { base } from '$app/paths'
import AppConnectInner from '$lib/components/AppConnectInner.svelte'
import DarkModeObserver from '$lib/components/DarkModeObserver.svelte'
import { Button } from '$lib/components/common'
@@ -13,7 +11,6 @@
let step = 1
let disabled = false
let isGoogleSignin = false
let manual = true
let appConnect: AppConnectInner | undefined = undefined
@@ -40,32 +37,21 @@
<Button variant="border" on:click={appConnect?.back}>Back</Button>
{/if}
{#if isGoogleSignin}
<button {disabled} on:click={appConnect?.next}>
<img
class="h-10 w-auto object-contain"
src={darkMode ? base + '/google_signin_dark.png' : base + '/google_signin_light.png'}
alt="Google sign-in"
/>
</button>
{:else}
<Button {disabled} on:click={appConnect?.next}>
{#if step == 2 && !manual}
Connect
{:else if step == 1}
Next
{:else}
Save
{/if}
</Button>
{/if}
<Button {disabled} on:click={appConnect?.next}>
{#if step == 2 && !manual}
Connect
{:else if step == 1}
Next
{:else}
Save
{/if}
</Button>
</div>
</div>
<AppConnectInner
bind:this={appConnect}
bind:step
bind:resourceType
bind:isGoogleSignin
bind:disabled
bind:manual
on:error
@@ -36,7 +36,7 @@
runPreview($previewArgs, undefined)
}
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, initialPath } =
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, initialPath, customUi } =
getContext<FlowEditorContext>('FlowEditorContext')
const dispatch = createEventDispatcher()
@@ -326,6 +326,7 @@
<div class="pt-4 flex flex-col grow">
{#if jobId}
<FlowStatusViewer
hideDownloadInGraph={customUi?.downloadLogs === false}
wideResults
{flowStateStore}
{jobId}
@@ -1,15 +1,418 @@
<script lang="ts">
import { Drawer, DrawerContent } from './common'
import { onMount } from 'svelte'
import { Drawer, DrawerContent, Button } from './common'
import QueueMetricsDrawerInner from './QueueMetricsDrawerInner.svelte'
import { ConfigService } from '$lib/gen'
import Section from './Section.svelte'
import { sendUserToast } from '$lib/toast'
import { Pencil, Trash, Check, PlusCircle, SaveIcon } from 'lucide-svelte'
import Tooltip from './Tooltip.svelte'
import { enterpriseLicense } from '$lib/stores'
type Alert = {
name: string
tags_to_monitor: string[]
jobs_num_threshold: number
alert_cooldown_seconds: number
alert_time_threshold_seconds: number
}
let drawer: Drawer
export function openDrawer() {
drawer?.openDrawer()
}
let alerts: Alert[] = []
let configName = 'alert__job_queue_waiting'
let originalAlerts: Alert[] = []
let newTag = ''
let editingIndex = -1
let changesMade = false
let removedAlerts: Alert[] = []
let stagedNewAlert = false
let workerTags: string[] = []
let filteredTags: string[] = []
$: removedAlerts
onMount(async () => {
await fetchConfig()
workerTags = await fetchWorkerTags()
})
async function fetchConfig() {
try {
const response = (await ConfigService.getConfig({ name: configName })) as { alerts: Alert[] }
alerts = response.alerts || []
originalAlerts = JSON.parse(JSON.stringify(alerts))
} catch (error) {
console.error('Failed to fetch config:', error)
}
}
async function fetchWorkerTags(): Promise<string[]> {
try {
const response = await ConfigService.listConfigs()
const workerTagsSet = new Set<string>()
response.forEach((config) => {
if (config.name.startsWith('worker__') && Array.isArray(config.config?.worker_tags)) {
config?.config?.worker_tags.forEach((tag) => workerTagsSet.add(tag))
}
})
return Array.from(workerTagsSet)
} catch (error) {
console.error('Failed to fetch worker tags:', error)
return []
}
}
function startEditing(index) {
editingIndex = index
updateWorkerTags()
}
function saveAlert(index) {
const newAlert = alerts[index]
if (newAlert.tags_to_monitor.length === 0) {
sendUserToast('Please add at least one tag before saving.', true)
return
}
if (
newAlert.jobs_num_threshold <= 0 ||
newAlert.alert_cooldown_seconds <= 0 ||
newAlert.alert_time_threshold_seconds <= 0
) {
sendUserToast('All numeric values must be strictly positive.', true)
return
}
const alertExists = originalAlerts.some(
(alert) =>
originalAlerts.indexOf(alert) !== index &&
JSON.stringify(alert.tags_to_monitor.sort()) ===
JSON.stringify(newAlert.tags_to_monitor.sort())
)
if (alertExists) {
sendUserToast('An identical alert already exists.', true)
return
}
editingIndex = -1
changesMade = JSON.stringify(alerts) !== JSON.stringify(originalAlerts)
stagedNewAlert = false
}
function stageDeleteAlert(index) {
const alert = alerts[index]
removedAlerts = [...removedAlerts, alert]
changesMade =
removedAlerts.length > 0 || JSON.stringify(alerts) !== JSON.stringify(originalAlerts)
}
function filterTags(event: Event) {
const input = (event.target as HTMLInputElement).value
filteredTags = workerTags.filter((tag) => tag.toLowerCase().includes(input.toLowerCase()))
}
function addTag(alertIndex, tag) {
if (workerTags.includes(tag) && !alerts[alertIndex].tags_to_monitor.includes(tag)) {
alerts[alertIndex].tags_to_monitor = [...alerts[alertIndex].tags_to_monitor, tag]
}
newTag = ''
filteredTags = []
}
function removeTag(alertIndex, tag) {
alerts[alertIndex].tags_to_monitor = alerts[alertIndex].tags_to_monitor.filter((t) => t !== tag)
}
async function applyConfig() {
if (stagedNewAlert) {
sendUserToast('New alert staged. Please save or cancel the changes before applying.', true)
return
}
try {
await ConfigService.updateConfig({ name: configName, requestBody: { alerts } })
sendUserToast('Configuration updated successfully')
alerts = alerts.filter((alert) => !removedAlerts.includes(alert))
originalAlerts = JSON.parse(JSON.stringify(alerts))
removedAlerts = []
changesMade = false
stagedNewAlert = false
} catch (error) {
console.error('Failed to update config:', error)
}
}
async function cancelChanges() {
alerts = [...alerts, ...removedAlerts]
alerts = JSON.parse(JSON.stringify(originalAlerts))
removedAlerts = []
editingIndex = -1
changesMade = false
stagedNewAlert = false
}
function addNewAlert() {
// alert already being added
if (stagedNewAlert) {
return
}
const newAlert = {
name: 'New Alert',
tags_to_monitor: [],
jobs_num_threshold: 3,
alert_cooldown_seconds: 600,
alert_time_threshold_seconds: 30
}
alerts = [...alerts, newAlert]
editingIndex = alerts.length - 1
stagedNewAlert = true
updateWorkerTags()
}
async function updateWorkerTags() {
workerTags = await fetchWorkerTags()
}
function addAllTags(alertIndex) {
alerts[alertIndex].tags_to_monitor = [
...new Set([...alerts[alertIndex].tags_to_monitor, ...workerTags])
]
alerts = alerts // Trigger reactivity
}
</script>
<Drawer bind:this={drawer}>
<Drawer bind:this={drawer} size="800px">
<DrawerContent title="Queue Metrics" on:close={drawer.closeDrawer}>
<QueueMetricsDrawerInner />
<Section
label="Queue Alert Settings"
collapsable={true}
tooltip="Manage queue alerts for monitoring queue metrics. These alerts will trigger a critical alert notification if the queue metrics are met."
eeOnly={true}
>
{#if $enterpriseLicense}
{#if changesMade}
<div class="text-red-600 text-xs whitespace-nowrap pb-2">Non applied changes</div>
<div class="flex gap-2 pb-2">
<Button color="blue" size="xs" on:click={applyConfig}>
<SaveIcon size={16} /> Apply Config
</Button>
<Button color="light" size="xs" on:click={cancelChanges}>Cancel</Button>
</div>
{/if}
{#if alerts.length > 0}
<div>
<form on:submit|preventDefault>
<table class="w-full border-collapse mb-2 text-xs table-auto">
<thead class="bg-gray-200 text-left text-xs">
<tr>
<th class="p-2 min-w-[120px]">
Name
<Tooltip
markdownTooltip="The name of the alert. This will be used in the crticial alert notification and help identify the alert."
/>
</th>
<th class="p-2 w-full">
Queue Tags to Monitor
<Tooltip markdownTooltip="Queue tags to monitor for this alert." />
</th>
<th class="p-2 min-w-[65px]">
Jobs
<Tooltip
markdownTooltip="Number of jobs threshold. An alert will be triggerd if more than this number of jobs are in the queue for at least the time threshold."
/>
</th>
<th class="p-2 min-w-[115px]">
Cooldown (s)
<Tooltip
markdownTooltip="Cooldown period in seconds. The cooldown period is the time after an alert is triggered that no new alerts will be triggered."
/>
</th>
<th class="p-2 min-w-[105px]">
Time (s)
<Tooltip
markdownTooltip="Time threshold in seconds. An alert will be triggered if more than 'n jobs threshold' have been in the queue for at least this amount of time."
/>
</th>
<th class="p-2 min-w-[100px]"> Actions </th>
</tr>
</thead>
<tbody>
{#each alerts as alert, index}
<tr
class:staged-for-deletion={removedAlerts.includes(alert)}
class:non-interactable={removedAlerts.includes(alert)}
>
<td class="border p-2">
{#if editingIndex === index}
<input type="text" bind:value={alert.name} class="w-full p-1" />
{:else}
{alert.name}
{/if}
</td>
<td class="border p-2">
{#if editingIndex === index}
<div class="flex flex-wrap gap-1 mb-2">
{#each alert.tags_to_monitor as tag}
<span class="inline-block bg-blue-100 rounded px-2 py-1 text-xs">
{tag}
<button
on:click={() => removeTag(index, tag)}
aria-label="Remove Tag"
class="ml-1 text-black text-xs">x</button
>
</span>
{/each}
</div>
<div class="flex items-center">
<input
type="text"
bind:value={newTag}
placeholder="Add tag"
on:input={(e) => filterTags(e)}
class="p-1 flex-grow mr-1"
/>
<button on:click={() => addTag(index, newTag)} aria-label="Add Tag">
<PlusCircle size={16} />
</button>
</div>
<!-- Add the new "Add All Tags" button here -->
<button
on:click={() => addAllTags(index)}
class="text-xs hover:bg-gray-200 rounded px-2 py-1 mt-1"
>
Add All Tags
</button>
{#if filteredTags.length > 0}
<ul class="autocomplete-list">
{#each filteredTags as tag}
<li>
<button type="button" on:click={() => addTag(index, tag)}
>{tag}</button
>
</li>
{/each}
</ul>
{/if}
{:else}
<div class="flex flex-wrap gap-1">
{#each alert.tags_to_monitor as tag}
<span class="inline-block bg-blue-100 rounded px-2 py-1 text-xs"
>{tag}</span
>
{/each}
</div>
{/if}
</td>
<td class="border p-2">
{#if editingIndex === index}
<input
type="number"
bind:value={alert.jobs_num_threshold}
class="w-full p-1"
/>
{:else}
{alert.jobs_num_threshold}
{/if}
</td>
<td class="border p-2">
{#if editingIndex === index}
<input
type="number"
bind:value={alert.alert_cooldown_seconds}
class="w-full p-1"
/>
{:else}
{alert.alert_cooldown_seconds}
{/if}
</td>
<td class="border p-2">
{#if editingIndex === index}
<input
type="number"
bind:value={alert.alert_time_threshold_seconds}
class="w-full p-1"
/>
{:else}
{alert.alert_time_threshold_seconds}
{/if}
</td>
<td class="border p-2">
<div class="flex gap-3 justify-center items-center">
{#if editingIndex === index}
<button on:click={() => saveAlert(index)} aria-label="Save">
<Check size={16} />
</button>
{:else}
<button on:click={() => startEditing(index)} aria-label="Edit">
<Pencil size={16} />
</button>
<button on:click={() => stageDeleteAlert(index)} aria-label="Delete">
<Trash size={16} />
</button>
{/if}
</div>
</td>
</tr>
{/each}
</tbody>
</table>
</form>
</div>
{/if}
<!-- Button to Add New Alert at the Bottom of the Table -->
<div class="flex">
<Button color="blue" size="xs" on:click={addNewAlert}>
<PlusCircle size={16} />
Add New Alert
</Button>
</div>
{:else}
<p class="text-gray-600 text-sm">
Queue Metric Alerts are an enterprise feature allowing you to monitor queues for waiting jobs. Please upgrade to access this functionality.
<a href="https://www.windmill.dev/docs/misc/plans_details" target="_blank" class="text-blue-500 underline">Learn more about our plans.</a>
</p>
{/if}
</Section>
<div class="p-8">
<QueueMetricsDrawerInner />
</div>
</DrawerContent>
</Drawer>
<style>
.staged-for-deletion {
background-color: #f8d7da; /* Light red background for staged deletion */
}
.non-interactable {
pointer-events: none; /* Disable pointer events */
opacity: 0.5; /* Reduce opacity */
}
.autocomplete-list {
border: 1px solid #ccc;
max-height: 150px;
overflow-y: auto;
background-color: white;
position: absolute;
z-index: 1000;
}
.autocomplete-list li {
padding: 8px;
cursor: pointer;
}
.autocomplete-list li:hover {
background-color: #f0f0f0;
}
</style>
@@ -176,9 +176,8 @@
<Button
{disabled}
color="light"
variant="contained"
variant="border"
size="sm"
btnClasses="w-8 px-0.5 py-1.5"
on:click={() => appConnect?.open?.(resourceType, expressOAuthSetup)}
startIcon={{ icon: Plus }}
iconOnly={collection?.length > 0}
@@ -60,6 +60,7 @@ export async function main(s3: S3) {
},
body: JSON.stringify({
type: "S3",
region: s3.region,
bucket: s3.bucket,
endpoint: s3.endPoint,
port: s3.port,
+1
View File
@@ -30,6 +30,7 @@ export type FlowBuilderWhitelabelCustomUi = {
languages?: (SupportedLanguage | 'docker' | 'bunnative')[]
scriptFork?: boolean
editorBar?: EditorBarUi
downloadLogs?: boolean
}
export type EditorBarUi = {
@@ -24,6 +24,7 @@
title="Deployment History"
>
<FlowHistoryInner
allowFork
on:historyRestore={() => {
drawer.closeDrawer()
dispatch('historyRestore')
@@ -9,6 +9,7 @@
import { createEventDispatcher } from 'svelte'
export let path: string
export let allowFork: boolean = false
let loading: boolean = false
let versions: FlowVersion[] = []
@@ -175,19 +176,22 @@
{/if}
</span>
<div class="flex p-1 gap-2">
<div class="flex">
<Button
size="sm"
color="dark"
on:click={() =>
window.open(
`/flows/add?template_id=${selectedVersion?.id}&template=${path}`,
'_blank'
)}
>
Restore as fork
</Button>
</div>
{#if allowFork}
<div class="flex">
<Button
size="sm"
color="dark"
on:click={() =>
window.open(
`/flows/add?template_id=${selectedVersion?.id}&template=${path}`,
'_blank'
)}
>
Restore as fork
</Button>
</div>
{/if}
<div class="flex">
<Button size="sm" color="dark" on:click={() => restoreVersion(selected)}
>Redeploy with that version
@@ -374,7 +374,7 @@
class={advancedSelected === 'runtime' ? 'h-[calc(100%-68px)]' : 'h-[calc(100%-34px)]'}
>
{#if selected === 'inputs' && (flowModule.value.type == 'rawscript' || flowModule.value.type == 'script' || flowModule.value.type == 'flow')}
<div class="h-full overflow-auto" id="flow-editor-step-input">
<div class="h-full overflow-auto px-2" id="flow-editor-step-input">
<PropPickerWrapper
pickableProperties={stepPropPicker.pickableProperties}
error={failureModule}
@@ -6,9 +6,9 @@
import { filteredContentForExport } from '../utils'
import YAML from 'yaml'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
import { Button } from '$lib/components/common'
import { sendUserToast } from '$lib/toast'
import { Loader2 } from 'lucide-svelte'
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
@@ -56,7 +56,11 @@
</svelte:fragment>
{#if $flowStore}
<SimpleEditor autoHeight bind:code lang="yaml" />
{#await import('../../SimpleEditor.svelte')}
<Loader2 class="animate-spin" />
{:then Module}
<Module.default autoHeight bind:code lang="yaml" />
{/await}
{/if}
</DrawerContent>
</Drawer>
@@ -7,7 +7,7 @@
export let spin: 'slow' | 'medium' | 'fast' | 'veryfast' | undefined = undefined
</script>
{#if customIcon}
{#if customIcon.white || customIcon.normal}
{#if white}
<img src={customIcon.white} alt="Windmill Custom icon" {width} {height} class={$$props.class} />
{:else}
+1 -1
View File
@@ -1 +1 @@
export let customIcon: { normal: string; white: string } | undefined = undefined
export let customIcon: { normal?: string; white?: string } = {}
+158 -111
View File
@@ -2,7 +2,7 @@ import { type Script } from './gen'
import type { SupportedLanguage } from './common'
export let PYTHON_FAILURE_MODULE_CODE = `import os
const PYTHON_FAILURE_MODULE_CODE = `import os
def main(message: str, name: str, step_id: str):
flow_id = os.environ.get("WM_ROOT_FLOW_JOB_ID")
@@ -11,13 +11,13 @@ def main(message: str, name: str, step_id: str):
print("step_id", step_id)
return { "message": message, "flow_id": flow_id, "step_id": step_id, "recover": False }`
export let PYTHON_INIT_CODE_CLEAR = `# import wmill
const PYTHON_INIT_CODE_CLEAR = `# import wmill
def main(x: str):
return x`
export let PYTHON_INIT_CODE_TRIGGER = `import wmill
const PYTHON_INIT_CODE_TRIGGER = `import wmill
def main():
@@ -32,7 +32,7 @@ def main():
# return range from (state to newState)
return [1, 2, 3]`
export let PYTHON_INIT_CODE = `import os
const PYTHON_INIT_CODE = `import os
import wmill
# You can import any PyPi package.
@@ -73,7 +73,7 @@ def main(
# return value is converted to JSON
return {"splitted": name.split(), "user": user, "state": new_state}`
export let NATIVETS_INIT_CODE = `// Fetch-only script, no imports allowed (except windmill) but benefits from a dedicated highly efficient runtime
const NATIVETS_INIT_CODE = `// Fetch-only script, no imports allowed (except windmill) but benefits from a dedicated highly efficient runtime
//import * as wmill from './windmill.ts'
export async function main(example_input: number = 3) {
@@ -85,7 +85,7 @@ export async function main(example_input: number = 3) {
}
`
export let BUNNATIVE_INIT_CODE = `//native
const BUNNATIVE_INIT_CODE = `//native
//you can add proxy support using //proxy http(s)://host:port
// native scripts are bun scripts that are executed on native workers and can be parallelized
@@ -102,7 +102,7 @@ export async function main(example_input: number = 3) {
}
`
export let NATIVETS_INIT_CODE_CLEAR = `// Fetch-only script, no imports allowed (except windmill) but benefits from a dedicated highly efficient runtime
const NATIVETS_INIT_CODE_CLEAR = `// Fetch-only script, no imports allowed (except windmill) but benefits from a dedicated highly efficient runtime
//import * as wmill from './windmill.ts'
export async function main() {
@@ -113,7 +113,7 @@ export async function main() {
}
`
export let DENO_INIT_CODE = `// Ctrl/CMD+. to cache dependencies on imports hover.
const DENO_INIT_CODE = `// Ctrl/CMD+. to cache dependencies on imports hover.
// Deno uses "npm:" prefix to import from npm (https://deno.land/manual@v1.36.3/node/npm_specifiers)
// import * as wmill from "npm:windmill-client@${__pkg__.version}"
@@ -134,7 +134,7 @@ export async function main(
}
`
export let BUN_INIT_CODE = `// there are multiple modes to add as header: //nobundling //native //npm //nodejs
const BUN_INIT_CODE = `// there are multiple modes to add as header: //nobundling //native //npm //nodejs
// https://www.windmill.dev/docs/getting_started/scripts_quickstart/typescript#modes
// import { toWords } from "number-to-words@1"
@@ -165,7 +165,7 @@ export async function main(
}
`
export let GO_INIT_CODE = `package inner
const GO_INIT_CODE = `package inner
import (
"fmt"
@@ -189,7 +189,7 @@ func main(x string, nested struct {
}
`
export let GO_FAILURE_MODULE_CODE = `package inner
const GO_FAILURE_MODULE_CODE = `package inner
import (
"fmt"
@@ -206,21 +206,21 @@ func main(message string, name string) (interface{}, error) {
}
`
export let DENO_INIT_CODE_CLEAR = `// import * as wmill from "npm:windmill-client@${__pkg__.version}"
const DENO_INIT_CODE_CLEAR = `// import * as wmill from "npm:windmill-client@${__pkg__.version}"
export async function main(x: string) {
return x
}
`
export let BUN_INIT_CODE_CLEAR = `// import * as wmill from "windmill-client"
const BUN_INIT_CODE_CLEAR = `// import * as wmill from "windmill-client"
export async function main(x: string) {
return x
}
`
export let DENO_FAILURE_MODULE_CODE = `
const DENO_FAILURE_MODULE_CODE = `
export async function main(message: string, name: string, step_id: string) {
const flow_id = Deno.env.get("WM_ROOT_FLOW_JOB_ID")
console.log("message", message)
@@ -230,7 +230,7 @@ export async function main(message: string, name: string, step_id: string) {
}
`
export let BUN_FAILURE_MODULE_CODE = `
const BUN_FAILURE_MODULE_CODE = `
export async function main(message: string, name: string, step_id: string) {
const flow_id = process.env.WM_ROOT_FLOW_JOB_ID
console.log("message", message)
@@ -240,7 +240,7 @@ export async function main(message: string, name: string, step_id: string) {
}
`
export let POSTGRES_INIT_CODE = `-- to pin the database use '-- database f/your/path'
const POSTGRES_INIT_CODE = `-- to pin the database use '-- database f/your/path'
-- $1 name1 = default arg
-- $2 name2
-- $3 name3
@@ -249,7 +249,7 @@ INSERT INTO demo VALUES (\$1::TEXT, \$2::INT, \$3::TEXT[]) RETURNING *;
UPDATE demo SET col2 = \$4::INT WHERE col2 = \$2::INT;
`
export let MYSQL_INIT_CODE = `-- to pin the database use '-- database f/your/path'
const MYSQL_INIT_CODE = `-- to pin the database use '-- database f/your/path'
-- :name1 (text) = default arg
-- :name2 (int)
-- :name3 (int)
@@ -257,7 +257,7 @@ INSERT INTO demo VALUES (:name1, :name2);
UPDATE demo SET col2 = :name3 WHERE col2 = :name2;
`
export let BIGQUERY_INIT_CODE = `-- to pin the database use '-- database f/your/path'
const BIGQUERY_INIT_CODE = `-- to pin the database use '-- database f/your/path'
-- @name1 (string) = default arg
-- @name2 (integer)
-- @name3 (string[])
@@ -266,7 +266,7 @@ INSERT INTO \`demodb.demo\` VALUES (@name1, @name2, @name3);
UPDATE \`demodb.demo\` SET col2 = @name4 WHERE col2 = @name2;
`
export let SNOWFLAKE_INIT_CODE = `-- to pin the database use '-- database f/your/path'
const SNOWFLAKE_INIT_CODE = `-- to pin the database use '-- database f/your/path'
-- ? name1 (varchar) = default arg
-- ? name2 (int)
INSERT INTO demo VALUES (?, ?);
@@ -275,7 +275,7 @@ INSERT INTO demo VALUES (?, ?);
UPDATE demo SET col2 = ? WHERE col2 = ?;
`
export let MSSQL_INIT_CODE = `-- return_last_result
const MSSQL_INIT_CODE = `-- return_last_result
-- to pin the database use '-- database f/your/path'
-- @p1 name1 (varchar) = default arg
-- @p2 name2 (int)
@@ -284,7 +284,7 @@ INSERT INTO demo VALUES (@p1, @p2);
UPDATE demo SET col2 = @p3 WHERE col2 = @p2;
`
export let GRAPHQL_INIT_CODE = `query($name4: String, $name2: Int, $name3: [String]) {
const GRAPHQL_INIT_CODE = `query($name4: String, $name2: Int, $name3: [String]) {
demo(name1: $name1, name2: $name2, name3: $name3) {
name1,
name2,
@@ -293,7 +293,7 @@ export let GRAPHQL_INIT_CODE = `query($name4: String, $name2: Int, $name3: [Stri
}
`
export let PHP_INIT_CODE = `<?php
const PHP_INIT_CODE = `<?php
// remove the first // of the following lines to specify packages to install using composer
// // require:
@@ -313,7 +313,7 @@ function main(
}
`
export let RUST_INIT_CODE = `//! Add dependencies in the following partial Cargo.toml manifest
const RUST_INIT_CODE = `//! Add dependencies in the following partial Cargo.toml manifest
//!
//! \`\`\`cargo
//! [dependencies]
@@ -348,7 +348,7 @@ fn main(who_to_greet: String, numbers: Vec<i8>) -> anyhow::Result<Ret> {
}
`
export let FETCH_INIT_CODE = `export async function main(
const FETCH_INIT_CODE = `export async function main(
url: string | undefined,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' = 'GET',
body: Object = {},
@@ -379,7 +379,7 @@ export let FETCH_INIT_CODE = `export async function main(
})
}`
export let BASH_INIT_CODE = `# shellcheck shell=bash
const BASH_INIT_CODE = `# shellcheck shell=bash
# arguments of the form X="$I" are parsed as parameters X of type string
msg="$1"
dflt="\${2:-default value}"
@@ -389,7 +389,7 @@ dflt="\${2:-default value}"
echo "Hello $msg"
`
export let DENO_INIT_CODE_TRIGGER = `import * as wmill from "npm:windmill-client@${__pkg__.version}"
const DENO_INIT_CODE_TRIGGER = `import * as wmill from "npm:windmill-client@${__pkg__.version}"
export async function main() {
@@ -410,7 +410,7 @@ export async function main() {
}
`
export let BUN_INIT_CODE_TRIGGER = `import * as wmill from "windmill-client"
const BUN_INIT_CODE_TRIGGER = `import * as wmill from "windmill-client"
export async function main() {
@@ -431,7 +431,7 @@ export async function main() {
}
`
export let GO_INIT_CODE_TRIGGER = `package inner
const GO_INIT_CODE_TRIGGER = `package inner
import (
wmill "github.com/windmill-labs/windmill-go-client"
@@ -455,7 +455,7 @@ func main() (interface{}, error) {
}
`
export let DENO_INIT_CODE_APPROVAL = `import * as wmill from "npm:windmill-client@^1.158.2"
const DENO_INIT_CODE_APPROVAL = `import * as wmill from "npm:windmill-client@^1.158.2"
export async function main(approver?: string) {
const urls = await wmill.getResumeUrls(approver)
@@ -483,7 +483,7 @@ export async function main(approver?: string) {
// add a form in Advanced - Suspend
// all on approval steps: https://www.windmill.dev/docs/flows/flow_approval`
export let BUN_INIT_CODE_APPROVAL = `import * as wmill from "windmill-client@^1.158.2"
const BUN_INIT_CODE_APPROVAL = `import * as wmill from "windmill-client@^1.158.2"
export async function main(approver?: string) {
const urls = await wmill.getResumeUrls(approver)
@@ -511,7 +511,7 @@ export async function main(approver?: string) {
// add a form in Advanced - Suspend
// all on approval steps: https://www.windmill.dev/docs/flows/flow_approval`
export let BUN_PREPROCESSOR_MODULE_CODE = `
const BUN_PREPROCESSOR_MODULE_CODE = `
export async function preprocessor(
wm_trigger: {
kind: 'http' | 'email' | 'webhook',
@@ -532,7 +532,7 @@ export async function preprocessor(
}
`
export let DENO_PREPROCESSOR_MODULE_CODE = `
const DENO_PREPROCESSOR_MODULE_CODE = `
export async function preprocessor(
wm_trigger: {
kind: 'http' | 'email' | 'wehbook',
@@ -553,7 +553,7 @@ export async function preprocessor(
}
`
export let PYTHON_INIT_CODE_APPROVAL = `import wmill
const PYTHON_INIT_CODE_APPROVAL = `import wmill
def main():
urls = wmill.get_resume_urls()
@@ -580,7 +580,7 @@ def main():
# add a form in Advanced - Suspend
# all on approval steps: https://www.windmill.dev/docs/flows/flow_approval`
export let PYTHON_PREPROCESSOR_MODULE_CODE = `from typing import TypedDict, Literal
const PYTHON_PREPROCESSOR_MODULE_CODE = `from typing import TypedDict, Literal
class Http(TypedDict):
route: str # The route path, e.g. "/users/:id"
@@ -603,7 +603,7 @@ def preprocessor(
}
`
export let DOCKER_INIT_CODE = `# shellcheck shell=bash
const DOCKER_INIT_CODE = `# shellcheck shell=bash
# Bash script that calls docker as a client to the host daemon
# See documentation: https://www.windmill.dev/docs/advanced/docker
msg="\${1:-world}"
@@ -616,7 +616,7 @@ docker pull $IMAGE
docker run --rm $IMAGE $COMMAND
`
export let POWERSHELL_INIT_CODE = `param($Msg, $Dflt = "default value", [int]$Nb = 3)
const POWERSHELL_INIT_CODE = `param($Msg, $Dflt = "default value", [int]$Nb = 3)
# Import-Module MyModule
@@ -627,7 +627,7 @@ export let POWERSHELL_INIT_CODE = `param($Msg, $Dflt = "default value", [int]$Nb
# the last line of the stdout is the return value
Write-Output "Hello $Msg"`
export let ANSIBLE_PLAYBOOK_INIT_CODE = `---
const ANSIBLE_PLAYBOOK_INIT_CODE = `---
inventory:
- resource_type: ansible_inventory
# You can pin an inventory to this script by hardcoding the resource path:
@@ -671,43 +671,90 @@ dependencies:
content: "{{ my_result | to_json }}"
dest: result.json
`
const ALL_INITIAL_CODE = [
PYTHON_INIT_CODE,
PYTHON_INIT_CODE_TRIGGER,
DENO_INIT_CODE,
POSTGRES_INIT_CODE,
MYSQL_INIT_CODE,
BIGQUERY_INIT_CODE,
SNOWFLAKE_INIT_CODE,
MSSQL_INIT_CODE,
GRAPHQL_INIT_CODE,
DENO_INIT_CODE_TRIGGER,
DENO_INIT_CODE_CLEAR,
PYTHON_INIT_CODE_CLEAR,
PYTHON_FAILURE_MODULE_CODE,
PYTHON_PREPROCESSOR_MODULE_CODE,
DENO_INIT_CODE_APPROVAL,
DENO_FAILURE_MODULE_CODE,
DENO_PREPROCESSOR_MODULE_CODE,
BUN_INIT_CODE,
BUN_INIT_CODE_CLEAR,
BUN_INIT_CODE_APPROVAL,
BUN_FAILURE_MODULE_CODE,
BUN_PREPROCESSOR_MODULE_CODE,
BASH_INIT_CODE,
POWERSHELL_INIT_CODE,
PHP_INIT_CODE,
RUST_INIT_CODE,
ANSIBLE_PLAYBOOK_INIT_CODE
]
export const INITIAL_CODE = {
bun: {
script: BUN_INIT_CODE,
trigger: BUN_INIT_CODE_TRIGGER,
approval: BUN_INIT_CODE_APPROVAL,
failure: BUN_FAILURE_MODULE_CODE,
preprocessor: BUN_PREPROCESSOR_MODULE_CODE,
clear: BUN_INIT_CODE_CLEAR
},
python3: {
script: PYTHON_INIT_CODE,
trigger: PYTHON_INIT_CODE_TRIGGER,
approval: PYTHON_INIT_CODE_APPROVAL,
failure: PYTHON_FAILURE_MODULE_CODE,
preprocessor: PYTHON_PREPROCESSOR_MODULE_CODE,
clear: PYTHON_INIT_CODE_CLEAR
},
deno: {
script: DENO_INIT_CODE,
trigger: DENO_INIT_CODE_TRIGGER,
approval: DENO_INIT_CODE_APPROVAL,
failure: DENO_FAILURE_MODULE_CODE,
preprocessor: DENO_PREPROCESSOR_MODULE_CODE,
fetch: FETCH_INIT_CODE,
clear: DENO_INIT_CODE_CLEAR
},
go: {
script: GO_INIT_CODE,
trigger: GO_INIT_CODE_TRIGGER,
failure: GO_FAILURE_MODULE_CODE
},
bash: {
script: BASH_INIT_CODE
},
powershell: {
script: POWERSHELL_INIT_CODE
},
nativets: {
script: NATIVETS_INIT_CODE
},
postgresql: {
script: POSTGRES_INIT_CODE
},
mysql: {
script: MYSQL_INIT_CODE
},
bigquery: {
script: BIGQUERY_INIT_CODE
},
snowflake: {
script: SNOWFLAKE_INIT_CODE
},
mssql: {
script: MSSQL_INIT_CODE
},
graphql: {
script: GRAPHQL_INIT_CODE
},
php: {
script: PHP_INIT_CODE
},
rust: {
script: RUST_INIT_CODE
},
ansible: {
script: ANSIBLE_PLAYBOOK_INIT_CODE
},
docker: {
script: DOCKER_INIT_CODE
},
bunnative: {
script: BUNNATIVE_INIT_CODE
}
}
export function isInitialCode(content: string): boolean {
for (const code of ALL_INITIAL_CODE) {
if (content === code) {
return true
}
}
Object.values(INITIAL_CODE).forEach((lang) => {
Object.values(lang).forEach((code) => {
if (content === code) {
return true
}
})
})
return false
}
@@ -731,93 +778,93 @@ export function initialCode(
}
if (language === 'deno') {
if (kind === 'trigger') {
return DENO_INIT_CODE_TRIGGER
return INITIAL_CODE.deno.trigger
} else if (kind === 'script') {
if (subkind === 'flow') {
return DENO_INIT_CODE_CLEAR
return INITIAL_CODE.deno.clear
} else if (subkind === 'pgsql') {
return POSTGRES_INIT_CODE
return INITIAL_CODE.postgresql.script
} else if (subkind === 'mysql') {
return MYSQL_INIT_CODE
return INITIAL_CODE.mysql.script
} else if (subkind === 'fetch') {
return FETCH_INIT_CODE
return INITIAL_CODE.deno.fetch
} else if (subkind === 'preprocessor') {
return DENO_PREPROCESSOR_MODULE_CODE
return INITIAL_CODE.deno.preprocessor
} else {
return DENO_INIT_CODE
return INITIAL_CODE.deno.script
}
} else if (kind === 'failure') {
return DENO_FAILURE_MODULE_CODE
return INITIAL_CODE.deno.failure
} else if (kind === 'approval') {
return DENO_INIT_CODE_APPROVAL
return INITIAL_CODE.deno.approval
} else {
return DENO_INIT_CODE
return INITIAL_CODE.deno.script
}
} else if (language === 'python3') {
if (kind === 'trigger') {
return PYTHON_INIT_CODE_TRIGGER
return INITIAL_CODE.python3.trigger
} else if (kind === 'approval') {
return PYTHON_INIT_CODE_APPROVAL
return INITIAL_CODE.python3.approval
} else if (kind === 'failure') {
return PYTHON_FAILURE_MODULE_CODE
return INITIAL_CODE.python3.failure
} else if (subkind === 'flow') {
return PYTHON_INIT_CODE_CLEAR
return INITIAL_CODE.python3.clear
} else if (subkind === 'preprocessor') {
return PYTHON_PREPROCESSOR_MODULE_CODE
return INITIAL_CODE.python3.preprocessor
} else {
return PYTHON_INIT_CODE
return INITIAL_CODE.python3.script
}
} else if (language == 'bash') {
if (subkind === 'docker') {
return DOCKER_INIT_CODE
return INITIAL_CODE.docker.script
} else {
return BASH_INIT_CODE
return INITIAL_CODE.bash.script
}
} else if (language == 'powershell') {
return POWERSHELL_INIT_CODE
return INITIAL_CODE.powershell.script
} else if (language == 'nativets') {
return NATIVETS_INIT_CODE
return INITIAL_CODE.nativets.script
} else if (language == 'postgresql') {
return POSTGRES_INIT_CODE
return INITIAL_CODE.postgresql.script
} else if (language == 'mysql') {
return MYSQL_INIT_CODE
return INITIAL_CODE.mysql.script
} else if (language == 'bigquery') {
return BIGQUERY_INIT_CODE
return INITIAL_CODE.bigquery.script
} else if (language == 'snowflake') {
return SNOWFLAKE_INIT_CODE
return INITIAL_CODE.snowflake.script
} else if (language == 'mssql') {
return MSSQL_INIT_CODE
return INITIAL_CODE.mssql.script
} else if (language == 'graphql') {
return GRAPHQL_INIT_CODE
return INITIAL_CODE.graphql.script
} else if (language == 'php') {
return PHP_INIT_CODE
return INITIAL_CODE.php.script
} else if (language == 'rust') {
return RUST_INIT_CODE
return INITIAL_CODE.rust.script
} else if (language == 'ansible') {
return ANSIBLE_PLAYBOOK_INIT_CODE
return INITIAL_CODE.ansible.script
} else if (language == 'bun' || language == 'bunnative') {
if (kind == 'trigger') {
return BUN_INIT_CODE_TRIGGER
return INITIAL_CODE.bun.trigger
} else if (language == 'bunnative' || subkind === 'bunnative') {
return BUNNATIVE_INIT_CODE
return INITIAL_CODE.bunnative.script
} else if (kind === 'approval') {
return BUN_INIT_CODE_APPROVAL
return INITIAL_CODE.bun.approval
} else if (kind === 'failure') {
return BUN_FAILURE_MODULE_CODE
return INITIAL_CODE.bun.failure
} else if (subkind === 'preprocessor') {
return BUN_PREPROCESSOR_MODULE_CODE
return INITIAL_CODE.bun.preprocessor
} else if (subkind === 'flow') {
return BUN_INIT_CODE_CLEAR
return INITIAL_CODE.bun.clear
}
return BUN_INIT_CODE
return INITIAL_CODE.bun.script
} else {
if (kind === 'failure') {
return GO_FAILURE_MODULE_CODE
return INITIAL_CODE.go.failure
} else if (kind === 'trigger') {
return GO_INIT_CODE_TRIGGER
return INITIAL_CODE.go.trigger
} else {
return GO_INIT_CODE
return INITIAL_CODE.go.script
}
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ ENV GOBIN=/usr/local/go/bin
RUN /usr/local/go/bin/go install -v golang.org/x/tools/gopls@latest
RUN pip3 install tornado python-lsp-jsonrpc ruff-lsp
COPY --from=denoland/deno:1.46.1 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=denoland/deno:2.0.0 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY Pipfile .
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.405.5"
wmill_pg = ">=1.405.5"
wmill = ">=1.406.0"
wmill_pg = ">=1.406.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.405.5
version: 1.406.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.405.5'
ModuleVersion = '1.406.0'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.405.5"
version = "1.406.0"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.405.5"
version = "1.406.0"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.405.5",
"version": "1.406.0",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.405.5",
"version": "1.406.0",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
+1 -1
View File
@@ -1 +1 @@
1.405.5
1.406.0