Compare commits

...

78 Commits

Author SHA1 Message Date
Clement Zhang
30ac3d3679 use array methods only on arrays 2024-11-18 16:15:12 +01:00
Clement Zhang
8fcf5de0b8 multiselect findOptionsByValue: JSON.parse 2024-11-18 15:11:46 +01:00
Clement Zhang
927dbc7b6d rm unnecessary parse 2024-11-18 12:32:06 +01:00
Clement Zhang
489d0f7926 better looking on empty string option label 2024-11-18 12:27:47 +01:00
Clement Zhang
82c2ac564a multiselect: new item key order {label, value} 2024-11-18 12:12:51 +01:00
Clement Zhang
ce01d975b3 multiselect: handle duplicates (do not allow) 2024-11-18 12:09:42 +01:00
Clement Zhang
74250285fc object/array as default item 2024-11-18 11:10:15 +01:00
Clement Zhang
3c50c0ffa3 Merge branch 'main' into feat/appeditor-multiselect 2024-11-18 10:50:45 +01:00
Ruben Fiszel
e4583e9b23 fix cli handling of ai resource generalization 2024-11-16 11:27:01 +01:00
Ruben Fiszel
93063b1c50 chore(main): release 1.425.1 (#4728)
* chore(main): release 1.425.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-11-16 10:04:30 +01:00
HugoCasa
17d893315b fix: revert bool to text support in pg (#4727) 2024-11-16 10:01:16 +01:00
Ruben Fiszel
0acd1b4a99 fix error logs on empty get_logs 2024-11-16 09:30:36 +01:00
Ruben Fiszel
4b04cc4d1a chore(main): release 1.425.0 (#4715)
* chore(main): release 1.425.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-11-15 22:31:49 +01:00
HugoCasa
d999e91583 unbreak nullable inputs in pg (#4726) 2024-11-15 22:27:28 +01:00
pyranota
9065716020 Fix ignore confirmation modal if no changes (#4725) 2024-11-15 19:47:09 +01:00
pyranota
56f70fd7de Ignore fields with false value in DiffViewer (#4712)
* Convert false to undefined

* Remove comments

* Make it consistent

* Formatting

* Apply at downstream
2024-11-15 19:24:57 +01:00
HugoCasa
9ad1afc8f2 unbreak ::text for json in pg (#4722)
* unbreak ::text for json in pg

* better error handling
2024-11-15 18:35:21 +01:00
Clement Zhang
e783f2487a Merge branch 'main' into feat/appeditor-multiselect 2024-11-15 18:14:46 +01:00
Alexander Petric
c069969732 cloud_hosted non ee scope (#4723) 2024-11-15 18:14:11 +01:00
Clement Zhang
985846f312 parse 2024-11-15 17:58:04 +01:00
Clement Zhang
1a6f89df21 appselect: render (empty string), check for equal value 2024-11-15 17:00:25 +01:00
Henri Courdent
9ce1d46459 Result node placeholder (#4721) 2024-11-15 16:55:53 +01:00
Alexander Petric
ff5fcc59d4 if CLOUD_HOSTED and workspace_id set, autoacknowledge for superadmin (#4719) 2024-11-15 16:31:42 +01:00
Clement Zhang
552a0f8ec8 Merge branch 'feat/appeditor-multiselect' of github.com:windmill-labs/windmill into feat/appeditor-multiselect 2024-11-15 16:15:04 +01:00
Clement Zhang
5abac8bc2b result is any[], rm stringify 2024-11-15 16:12:19 +01:00
Ruben Fiszel
200112cf1f Merge branch 'main' into feat/appeditor-multiselect 2024-11-15 15:51:58 +01:00
Clement Zhang
4fc09da15d Merge branch 'feat/appeditor-multiselect' of github.com:windmill-labs/windmill into feat/appeditor-multiselect 2024-11-15 10:13:22 +01:00
Clement Zhang
2df7c85e2d result as string[] 2024-11-15 10:09:14 +01:00
Clement Zhang
686dc177e8 multiselect: keep defaultItems (no breaking change) 2024-11-15 10:09:14 +01:00
Clement Zhang
88d75cc584 add multiselect novelties to <AppSelect />, harmonize, clean <AppSelect /> 2024-11-15 10:09:14 +01:00
Clement Zhang
1367296de0 cleaning, renaming 2024-11-15 10:09:14 +01:00
Clement Zhang
51d5987110 rm seamingly unused vars 2024-11-15 10:09:14 +01:00
Clement Zhang
2fc808c065 every app components: switching from static to eval preserves the user data input 2024-11-15 10:09:14 +01:00
Clement Zhang
940af69a6a eval=>static preserves labeled state, wording (w/ label) 2024-11-15 10:09:14 +01:00
Clement Zhang
59a9458ca8 eval preserves labeled objects 2024-11-15 10:09:14 +01:00
Clement Zhang
756b07b16c setValue param is (option.value)[] 2024-11-15 10:09:14 +01:00
Clement Zhang
e2ebad90ba only use ObjectOption in AppMultiSelectv2 2024-11-15 10:09:14 +01:00
Clement Zhang
c724982d33 dirty fix 2024-11-15 10:09:14 +01:00
Clement Zhang
70737c6642 cleaning, doc 2024-11-15 10:09:14 +01:00
Clement Zhang
b84c76ee9f rm local output 2024-11-15 10:09:14 +01:00
Clement Zhang
663a193c2a clarify label when empty string 2024-11-15 10:09:14 +01:00
Clement Zhang
70b4bff32c avoid ghost options on labeled toggle, tiny cleaning 2024-11-15 10:09:14 +01:00
Clement Zhang
42bc803445 stable staticinputeditor! 2024-11-15 10:09:14 +01:00
Clement Zhang
a1038c32b7 WIP: unstable type handler 2024-11-15 10:09:14 +01:00
Clement Zhang
8e605a7f0f use the same editor as <Select /> 2024-11-15 10:09:14 +01:00
Clement Zhang
d6dc3be311 addressing ellipsis bot PR comments 2024-11-15 10:09:14 +01:00
Clement Zhang
3207a98d1a fix default items 2024-11-15 10:09:14 +01:00
Clement Zhang
629e1ec36b change input type 2024-11-15 10:09:14 +01:00
Clement Zhang
349be01861 Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-14 10:35:35 +01:00
Clement Zhang
d04620e86e result as string[] 2024-11-08 13:41:30 +01:00
Clement Zhang
d4a556d0f6 multiselect: keep defaultItems (no breaking change) 2024-11-08 11:26:42 +01:00
Clement Zhang
f350e3679b Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-07 16:42:04 +01:00
Clement Zhang
e338c28713 add multiselect novelties to <AppSelect />, harmonize, clean <AppSelect /> 2024-11-07 16:39:32 +01:00
Clement Zhang
8549cb20b0 cleaning, renaming 2024-11-07 14:52:02 +01:00
Clement Zhang
4b519a5dc8 rm seamingly unused vars 2024-11-07 14:32:24 +01:00
Clement Zhang
6b60ccc7ad Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-07 14:18:27 +01:00
Clement Zhang
34249143e7 every app components: switching from static to eval preserves the user data input 2024-11-07 14:18:14 +01:00
Clement Zhang
14ea9d6b55 eval=>static preserves labeled state, wording (w/ label) 2024-11-07 13:15:32 +01:00
Clement Zhang
eaa6e36f9d eval preserves labeled objects 2024-11-07 12:19:50 +01:00
Clement Zhang
fa9e4ba328 setValue param is (option.value)[] 2024-11-07 11:47:37 +01:00
Clement Zhang
322ed7dfd0 only use ObjectOption in AppMultiSelectv2 2024-11-07 11:10:07 +01:00
Clement Zhang
b91f4372e9 Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-07 10:32:48 +01:00
Clement Zhang
67e06d54f0 dirty fix 2024-11-06 18:45:07 +01:00
Clement Zhang
b5f3188239 cleaning, doc 2024-11-06 18:12:04 +01:00
Clement Zhang
6258c501a2 rm local output 2024-11-06 17:50:59 +01:00
Clement Zhang
e94c1f9a9d clarify label when empty string 2024-11-06 17:49:48 +01:00
Clement Zhang
d256f41793 Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-06 17:40:13 +01:00
Clement Zhang
7d52eecad8 avoid ghost options on labeled toggle, tiny cleaning 2024-11-06 17:37:10 +01:00
Clement Zhang
d74e406a2d stable staticinputeditor! 2024-11-06 16:53:25 +01:00
Clement Zhang
d9bd24df95 Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-06 10:45:45 +01:00
Clement Zhang
390e46245d WIP: unstable type handler 2024-11-05 18:39:15 +01:00
Clement Zhang
e1ea5bd2e8 Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-05 10:29:22 +01:00
Clement Zhang
546da5b983 use the same editor as <Select /> 2024-11-04 19:00:23 +01:00
Clement Zhang
29c9ba874c Merge remote-tracking branch 'upstream/main' into appeditor-multiselect-input-type 2024-11-04 18:01:44 +01:00
Clement Zhang
32f8127e02 addressing ellipsis bot PR comments 2024-11-04 17:53:50 +01:00
Clement Zhang
f875e74064 Merge branch 'main' into appeditor-multiselect-input-type 2024-11-04 17:37:24 +01:00
Clement Zhang
0ae0321e4b fix default items 2024-11-04 17:35:21 +01:00
Clement Zhang
9aee6f78d0 change input type 2024-11-04 16:44:53 +01:00
42 changed files with 1458 additions and 226 deletions

View File

@@ -1,5 +1,20 @@
# Changelog
## [1.425.1](https://github.com/windmill-labs/windmill/compare/v1.425.0...v1.425.1) (2024-11-16)
### Bug Fixes
* revert bool to text support in pg ([#4727](https://github.com/windmill-labs/windmill/issues/4727)) ([17d8933](https://github.com/windmill-labs/windmill/commit/17d893315bd86942c7895cdb3e9c3ab34977b258))
## [1.425.0](https://github.com/windmill-labs/windmill/compare/v1.424.0...v1.425.0) (2024-11-15)
### Features
* Handle `pip install` by `uv` ([#4517](https://github.com/windmill-labs/windmill/issues/4517)) ([f240d13](https://github.com/windmill-labs/windmill/commit/f240d1322a3c1669dfb69ebda6d05effcca26ae3))
* **monitoring:** workspace critical alerts ([#4684](https://github.com/windmill-labs/windmill/issues/4684)) ([c32038a](https://github.com/windmill-labs/windmill/commit/c32038a76d9d00703d8b865af72f083ac43414e3))
## [1.424.0](https://github.com/windmill-labs/windmill/compare/v1.423.2...v1.424.0) (2024-11-14)

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, job_logs.log_offset, job_logs.log_file_index\n FROM queue \n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.id = $1 AND queue.workspace_id = $2",
"query": "SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM queue \n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.id = $1 AND queue.workspace_id = $2",
"describe": {
"columns": [
{
@@ -33,9 +33,9 @@
"nullable": [
false,
null,
false,
null,
true
]
},
"hash": "57111a97ff906fd79ecd8a571212cc4fea9cb9c16fe950f082ad5469bfeefc82"
"hash": "b2c30137b3b64f8c3f6aea062e8687e119599641beee0a0d6c89c5d90e82d0db"
}

56
backend/Cargo.lock generated
View File

@@ -1032,9 +1032,9 @@ dependencies = [
[[package]]
name = "axum"
version = "0.7.7"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
checksum = "49c41b948da08fb481a94546cd874843adc1142278b0af4badf9b1b78599d68d"
dependencies = [
"async-trait",
"axum-core",
@@ -4831,9 +4831,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.162"
version = "0.2.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
checksum = "1fdaeca4cf44ed4ac623e86ef41f056e848dbeab7ec043ecb7326ba300b36fd0"
[[package]]
name = "libgit2-sys"
@@ -10610,7 +10610,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"axum",
@@ -10652,7 +10652,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"argon2",
@@ -10737,7 +10737,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -10755,7 +10755,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"chrono",
"serde",
@@ -10768,7 +10768,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"rsmq_async",
@@ -10783,7 +10783,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"async-stream",
@@ -10830,7 +10830,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"regex",
"rsmq_async",
@@ -10845,7 +10845,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"bytes",
@@ -10867,7 +10867,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"itertools 0.13.0",
"lazy_static",
@@ -10879,7 +10879,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -10888,7 +10888,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -10900,7 +10900,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"gosyn",
@@ -10912,7 +10912,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -10924,7 +10924,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"itertools 0.13.0",
@@ -10935,7 +10935,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"itertools 0.13.0",
@@ -10946,7 +10946,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"async-recursion",
@@ -10964,7 +10964,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -10981,7 +10981,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -10993,7 +10993,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -11011,7 +11011,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"getrandom 0.2.15",
@@ -11032,7 +11032,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"serde_json",
@@ -11042,7 +11042,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"async-recursion",
@@ -11075,7 +11075,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -11085,7 +11085,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.424.0"
version = "1.425.1"
dependencies = [
"anyhow",
"async-recursion",

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.424.0"
version = "1.425.1"
authors.workspace = true
edition.workspace = true
@@ -29,7 +29,7 @@ members = [
]
[workspace.package]
version = "1.424.0"
version = "1.425.1"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.424.0
version: 1.425.1
title: Windmill API
contact:

View File

@@ -950,7 +950,7 @@ async fn get_job_logs(
Ok(content_plain(Body::from(logs)))
} else {
let text = sqlx::query!(
"SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, job_logs.log_offset, job_logs.log_file_index
"SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index
FROM queue
LEFT JOIN job_logs ON job_logs.job_id = queue.id
WHERE queue.id = $1 AND queue.workspace_id = $2",
@@ -971,10 +971,10 @@ async fn get_job_logs(
log_job_view(&db, opt_authed.as_ref(), &w_id, &id).await?;
#[cfg(all(feature = "enterprise", feature = "parquet"))]
if let Some(r) = get_logs_from_store(text.log_offset, &logs, &text.log_file_index).await {
if let Some(r) = get_logs_from_store(text.log_offset.unwrap_or(0), &logs, &text.log_file_index).await {
return r.map(content_plain);
}
if let Some(r) = get_logs_from_disk(text.log_offset, &logs, &text.log_file_index).await {
if let Some(r) = get_logs_from_disk(text.log_offset.unwrap_or(0), &logs, &text.log_file_index).await {
return r.map(content_plain);
}

View File

@@ -32,7 +32,6 @@ pub const GIT_VERSION: &str =
use crate::CRITICAL_ALERT_MUTE_UI_ENABLED;
use std::sync::atomic::Ordering;
#[cfg(feature = "enterprise")]
use crate::worker::CLOUD_HOSTED;
lazy_static::lazy_static! {
@@ -276,7 +275,7 @@ pub async fn report_critical_error(
// we ack_global if mute_global is true, or if mute_workspace is true
// but we ignore global mute setting for ack_workspace
let acknowledge_workspace = mute_workspace;
let acknowledge_global = mute_global || mute_workspace;
let acknowledge_global = mute_global || mute_workspace || ( workspace_id.is_some() && *CLOUD_HOSTED);
if let Err(err) = sqlx::query!(
"INSERT INTO alerts (alert_type, message, acknowledged, acknowledged_workspace, workspace_id, resource)

View File

@@ -416,24 +416,32 @@ pub async fn do_postgresql(
}
fn map_as_single_type<T>(
vec: &Vec<Value>,
vec: Option<&Vec<Value>>,
f: impl Fn(&Value) -> Option<T>,
) -> anyhow::Result<Vec<Option<T>>> {
vec.into_iter()
.map(|v| {
// allow nulls in arrays
if matches!(v, Value::Null) {
Some(None)
} else {
f(v).map(Some)
}
})
.collect::<Option<Vec<Option<T>>>>()
.ok_or_else(|| anyhow::anyhow!("Mixed types in array"))
) -> anyhow::Result<Option<Vec<Option<T>>>> {
if let Some(vec) = vec {
Ok(Some(
vec.into_iter()
.map(|v| {
// first option is if the value is of the right type (if none, will stop the collection and throw error)
// second option is if the value is null
// allow nulls in arrays
if matches!(v, Value::Null) {
Some(None)
} else {
f(v).map(Some)
}
})
.collect::<Option<Vec<Option<T>>>>()
.ok_or_else(|| anyhow::anyhow!("Mixed types in array"))?,
))
} else {
Ok(None)
}
}
fn convert_vec_val(
vec: &Vec<Value>,
vec: Option<&Vec<Value>>,
arg_t: &String,
) -> windmill_common::error::Result<Box<dyn ToSql + Sync + Send>> {
match arg_t.as_str() {
@@ -489,7 +497,9 @@ fn convert_vec_val(
.unwrap_or_default()
})
})?)),
"jsonb" | "json" => Ok(Box::new(vec.clone().into_iter().map(Some).collect_vec())),
"jsonb" | "json" => Ok(Box::new(
vec.map(|v| v.clone().into_iter().map(Some).collect_vec()),
)),
"bytea" => Ok(Box::new(map_as_single_type(vec, |v| {
v.as_str().map(|x| {
engine::general_purpose::STANDARD
@@ -512,9 +522,31 @@ fn convert_val(
match value {
Value::Array(vec) if arg_t.ends_with("[]") => {
let arg_t = arg_t.trim_end_matches("[]").to_string();
convert_vec_val(vec, &arg_t)
convert_vec_val(Some(vec), &arg_t)
}
Value::Null => Ok(Box::new(None::<bool>)),
Value::Null if arg_t.ends_with("[]") => {
let arg_t = arg_t.trim_end_matches("[]").to_string();
convert_vec_val(None, &arg_t)
}
Value::Null => match arg_t.as_str() {
"bool" | "boolean" => Ok(Box::new(None::<bool>)),
"char" | "character" => Ok(Box::new(None::<i8>)),
"smallint" | "smallserial" | "int2" | "serial2" => Ok(Box::new(None::<i16>)),
"int" | "integer" | "int4" | "serial" => Ok(Box::new(None::<i32>)),
"numeric" | "decimal" => Ok(Box::new(None::<Decimal>)),
"oid" => Ok(Box::new(None::<u32>)),
"bigint" | "bigserial" | "int8" | "serial8" => Ok(Box::new(None::<i64>)),
"real" | "float4" => Ok(Box::new(None::<f32>)),
"double" | "float8" => Ok(Box::new(None::<f64>)),
"uuid" => Ok(Box::new(None::<Uuid>)),
"date" => Ok(Box::new(None::<chrono::NaiveDate>)),
"time" | "timetz" => Ok(Box::new(None::<chrono::NaiveTime>)),
"timestamp" | "timestamptz" => Ok(Box::new(None::<chrono::NaiveDateTime>)),
"jsonb" | "json" => Ok(Box::new(None::<Option<Value>>)),
"bytea" => Ok(Box::new(None::<Vec<u8>>)),
"text" | "varchar" => Ok(Box::new(None::<String>)),
_ => Err(anyhow::anyhow!("Unsupported JSON null type"))?,
},
Value::Bool(b) => Ok(Box::new(b.clone())),
Value::Number(n) if matches!(typ, Typ::Str(_)) => Ok(Box::new(n.to_string())),
Value::Number(n) if arg_t == "char" && n.is_i64() => {
@@ -583,6 +615,11 @@ fn convert_val(
.unwrap_or(vec![]);
Ok(Box::new(bytes))
}
Value::Object(_) if arg_t == "text" || arg_t == "varchar" => {
Ok(Box::new(serde_json::to_string(value).map_err(|err| {
Error::ExecutionErr(format!("Failed to convert JSON to text: {}", err))
})?))
}
Value::Object(_) => Ok(Box::new(value.clone())),
Value::String(s) => Ok(Box::new(s.clone())),
_ => Err(Error::ExecutionErr(format!(

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.424.0";
export const VERSION = "v1.425.1";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({

View File

@@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = {
PASSWORD: undefined,
TOKEN: getEnv("WM_TOKEN"),
USERNAME: undefined,
VERSION: '1.407.2',
VERSION: '1.425.1',
WITH_CREDENTIALS: true,
interceptors: {
request: new Interceptors(),

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,10 @@
// This file is auto-generated by @hey-api/openapi-ts
export type AiResource = {
path: string;
provider: string;
};
export type Script = {
workspace_id?: string;
hash: string;
@@ -257,18 +262,21 @@ export type TruncatedToken = {
created_at: string;
last_used_at: string;
scopes?: Array<(string)>;
email?: string;
};
export type NewToken = {
label?: string;
expiration?: string;
scopes?: Array<(string)>;
workspace_id?: string;
};
export type NewTokenImpersonate = {
label?: string;
expiration?: string;
impersonate_email: string;
workspace_id?: string;
};
export type ListableVariable = {
@@ -317,7 +325,7 @@ export type AuditLog = {
id: number;
timestamp: string;
username: string;
operation: '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' | 'openai.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.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.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';
operation: '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: 'Created' | 'Updated' | 'Delete' | 'Execute';
resource?: string;
parameters?: {
@@ -325,7 +333,7 @@ export type AuditLog = {
};
};
export type operation = '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' | 'openai.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.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.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';
export type operation = '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';
export type action_kind = 'Created' | 'Updated' | 'Delete' | 'Execute';
@@ -542,6 +550,11 @@ export type HttpTrigger = {
edited_at: string;
script_path: string;
route_path: string;
static_asset_config?: {
s3: string;
storage?: string;
filename?: string;
};
is_flow: boolean;
extra_perms: {
[key: string]: (boolean);
@@ -559,6 +572,11 @@ export type NewHttpTrigger = {
path: string;
script_path: string;
route_path: string;
static_asset_config?: {
s3: string;
storage?: string;
filename?: string;
};
is_flow: boolean;
http_method: 'get' | 'post' | 'put' | 'delete' | 'patch';
is_async: boolean;
@@ -569,12 +587,89 @@ export type EditHttpTrigger = {
path: string;
script_path: string;
route_path?: string;
static_asset_config?: {
s3: string;
storage?: string;
filename?: string;
};
is_flow: boolean;
http_method: 'get' | 'post' | 'put' | 'delete' | 'patch';
is_async: boolean;
requires_auth: boolean;
};
export type TriggersCount = {
primary_schedule?: {
schedule?: string;
};
schedule_count?: number;
http_routes_count?: number;
webhook_count?: number;
email_count?: number;
websocket_count?: number;
};
export type WebsocketTrigger = {
path: string;
edited_by: string;
edited_at: string;
script_path: string;
url: string;
is_flow: boolean;
extra_perms: {
[key: string]: (boolean);
};
email: string;
workspace_id: string;
server_id?: string;
last_server_ping?: string;
error?: string;
enabled: boolean;
filters: Array<{
key: string;
value: unknown;
}>;
initial_messages: Array<WebsocketTriggerInitialMessage>;
url_runnable_args: ScriptArgs;
};
export type NewWebsocketTrigger = {
path: string;
script_path: string;
is_flow: boolean;
url: string;
enabled?: boolean;
filters: Array<{
key: string;
value: unknown;
}>;
initial_messages: Array<WebsocketTriggerInitialMessage>;
url_runnable_args: ScriptArgs;
};
export type EditWebsocketTrigger = {
url: string;
path: string;
script_path: string;
is_flow: boolean;
filters: Array<{
key: string;
value: unknown;
}>;
initial_messages: Array<WebsocketTriggerInitialMessage>;
url_runnable_args: ScriptArgs;
};
export type WebsocketTriggerInitialMessage = {
raw_message: string;
} | {
runnable_result: {
path: string;
args: ScriptArgs;
is_flow: boolean;
};
};
export type Group = {
name: string;
summary?: string;
@@ -660,6 +755,7 @@ export type GlobalUserInfo = {
name?: string;
company?: string;
username?: string;
operator_only?: boolean;
};
export type login_type = 'password' | 'github';
@@ -722,6 +818,9 @@ export type Policy = {
[key: string]: unknown;
};
};
s3_inputs?: Array<{
[key: string]: unknown;
}>;
execution_mode?: 'viewer' | 'publisher' | 'anonymous';
on_behalf_of?: string;
on_behalf_of_email?: string;
@@ -968,6 +1067,46 @@ export type JobSearchHit = {
dancer?: string;
};
export type LogSearchHit = {
dancer?: string;
};
export type AutoscalingEvent = {
id?: number;
worker_group?: string;
event_type?: string;
desired_workers?: number;
reason?: string;
applied_at?: string;
};
export type CriticalAlert = {
/**
* Unique identifier for the alert
*/
id?: number;
/**
* Type of alert (e.g., critical_error)
*/
alert_type?: string;
/**
* The message content of the alert
*/
message?: string;
/**
* Time when the alert was created
*/
created_at?: string;
/**
* Acknowledgment status of the alert, can be true, false, or null if not set
*/
acknowledged?: (boolean) | null;
/**
* Workspace id if the alert is in the scope of a workspace
*/
workspace_id?: (string) | null;
};
export type OpenFlow = {
summary: string;
description?: string;
@@ -1073,6 +1212,7 @@ export type RawScript = {
concurrent_limit?: number;
concurrency_time_window_s?: number;
custom_concurrency_key?: string;
is_trigger?: boolean;
};
export type language2 = 'deno' | 'bun' | 'python3' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'graphql' | 'nativets' | 'php';
@@ -1085,6 +1225,7 @@ export type PathScript = {
hash?: string;
type: 'script';
tag_override?: string;
is_trigger?: boolean;
};
export type PathFlow = {
@@ -1518,6 +1659,30 @@ export type SetPasswordData = {
export type SetPasswordResponse = (string);
export type SetPasswordForUserData = {
/**
* set password
*/
requestBody: {
password: string;
};
user: string;
};
export type SetPasswordForUserResponse = (string);
export type SetLoginTypeForUserData = {
/**
* set login type
*/
requestBody: {
login_type: string;
};
user: string;
};
export type SetLoginTypeForUserResponse = (string);
export type CreateUserGloballyData = {
/**
* user info
@@ -1675,6 +1840,7 @@ export type TestSmtpData = {
port: number;
from: string;
tls_implicit: boolean;
disable_tls: boolean;
};
};
};
@@ -1693,6 +1859,25 @@ export type TestCriticalChannelsData = {
export type TestCriticalChannelsResponse = (string);
export type GetCriticalAlertsData = {
acknowledged?: (boolean) | null;
page?: number;
pageSize?: number;
};
export type GetCriticalAlertsResponse = (Array<CriticalAlert>);
export type AcknowledgeCriticalAlertData = {
/**
* The ID of the critical alert to acknowledge
*/
id: number;
};
export type AcknowledgeCriticalAlertResponse = (string);
export type AcknowledgeAllCriticalAlertsResponse = (string);
export type TestLicenseKeyData = {
/**
* test license key
@@ -1917,6 +2102,10 @@ export type ExistsEmailData = {
export type ExistsEmailResponse = (boolean);
export type ListUsersAsSuperAdminData = {
/**
* filter only active users
*/
activeOnly?: boolean;
/**
* which page to return (start at 1, default 1)
*/
@@ -1952,7 +2141,7 @@ export type GetSettingsResponse = ({
customer_id?: string;
webhook?: string;
deploy_to?: string;
openai_resource_path?: string;
ai_resource?: AiResource;
code_completion_enabled: boolean;
error_handler?: string;
error_handler_extra_args?: ScriptArgs;
@@ -1962,6 +2151,7 @@ export type GetSettingsResponse = ({
deploy_ui?: WorkspaceDeployUISettings;
default_app?: string;
default_scripts?: WorkspaceDefaultScripts;
mute_critical_alerts?: boolean;
});
export type GetDeployToData = {
@@ -2070,7 +2260,7 @@ export type EditCopilotConfigData = {
* WorkspaceCopilotConfig
*/
requestBody: {
openai_resource_path?: string;
ai_resource?: AiResource;
code_completion_enabled: boolean;
};
workspace: string;
@@ -2083,7 +2273,8 @@ export type GetCopilotInfoData = {
};
export type GetCopilotInfoResponse = ({
exists_openai_resource_path: boolean;
ai_provider: string;
exists_ai_resource: boolean;
code_completion_enabled: boolean;
});
@@ -2219,6 +2410,15 @@ export type GetWorkspaceUsageData = {
export type GetWorkspaceUsageResponse = (number);
export type GetUsedTriggersData = {
workspace: string;
};
export type GetUsedTriggersResponse = ({
http_routes_used: boolean;
websocket_used: boolean;
});
export type ListUsersData = {
workspace: string;
};
@@ -2382,6 +2582,46 @@ export type ListContextualVariablesData = {
export type ListContextualVariablesResponse = (Array<ContextualVariable>);
export type WorkspaceGetCriticalAlertsData = {
acknowledged?: (boolean) | null;
page?: number;
pageSize?: number;
workspace: string;
};
export type WorkspaceGetCriticalAlertsResponse = (Array<CriticalAlert>);
export type WorkspaceAcknowledgeCriticalAlertData = {
/**
* The ID of the critical alert to acknowledge
*/
id: number;
workspace: string;
};
export type WorkspaceAcknowledgeCriticalAlertResponse = (string);
export type WorkspaceAcknowledgeAllCriticalAlertsData = {
workspace: string;
};
export type WorkspaceAcknowledgeAllCriticalAlertsResponse = (string);
export type WorkspaceMuteCriticalAlertsUiData = {
/**
* Boolean flag to mute critical alerts.
*/
requestBody: {
/**
* Whether critical alerts should be muted.
*/
mute_critical_alerts?: boolean;
};
workspace: string;
};
export type WorkspaceMuteCriticalAlertsUiResponse = (string);
export type LoginWithOauthData = {
clientName: string;
/**
@@ -2474,7 +2714,10 @@ export type DisconnectSlackData = {
export type DisconnectSlackResponse = (string);
export type ListOauthLoginsResponse = ({
oauth: Array<(string)>;
oauth: Array<{
type: string;
display_name?: string;
}>;
saml?: string;
});
@@ -3011,6 +3254,20 @@ export type GetScriptByPathData = {
export type GetScriptByPathResponse = (Script);
export type GetTriggersCountOfScriptData = {
path: string;
workspace: string;
};
export type GetTriggersCountOfScriptResponse = (TriggersCount);
export type ListTokensOfScriptData = {
path: string;
workspace: string;
};
export type ListTokensOfScriptResponse = (Array<TruncatedToken>);
export type GetScriptByPathWithDraftData = {
path: string;
workspace: string;
@@ -3025,6 +3282,13 @@ export type GetScriptHistoryByPathData = {
export type GetScriptHistoryByPathResponse = (Array<ScriptHistory>);
export type GetScriptLatestVersionData = {
path: string;
workspace: string;
};
export type GetScriptLatestVersionResponse = (ScriptHistory);
export type UpdateScriptHistoryData = {
hash: string;
path: string;
@@ -3378,6 +3642,13 @@ export type GetFlowHistoryData = {
export type GetFlowHistoryResponse = (Array<FlowVersion>);
export type GetFlowLatestVersionData = {
path: string;
workspace: string;
};
export type GetFlowLatestVersionResponse = (FlowVersion);
export type GetFlowVersionData = {
path: string;
version: number;
@@ -3408,6 +3679,20 @@ export type GetFlowByPathData = {
export type GetFlowByPathResponse = (Flow);
export type GetTriggersCountOfFlowData = {
path: string;
workspace: string;
};
export type GetTriggersCountOfFlowResponse = (TriggersCount);
export type ListTokensOfFlowData = {
path: string;
workspace: string;
};
export type ListTokensOfFlowResponse = (Array<TruncatedToken>);
export type ToggleWorkspaceErrorHandlerForFlowData = {
path: string;
/**
@@ -3637,6 +3922,13 @@ export type GetAppHistoryByPathData = {
export type GetAppHistoryByPathResponse = (Array<AppHistory>);
export type GetAppLatestVersionData = {
path: string;
workspace: string;
};
export type GetAppLatestVersionResponse = (AppHistory);
export type UpdateAppHistoryData = {
id: number;
/**
@@ -4099,6 +4391,16 @@ export type GetCompletedCountResponse = ({
database_length: number;
});
export type CountCompletedJobsData = {
allWorkspaces?: boolean;
completedAfterSAgo?: number;
success?: boolean;
tags?: string;
workspace: string;
};
export type CountCompletedJobsResponse = (number);
export type ListFilteredUuidsData = {
/**
* get jobs from all workspaces (only valid if request come from the `admins` workspace)
@@ -4888,11 +5190,80 @@ export type ExistsRouteData = {
export type ExistsRouteResponse = (boolean);
export type UsedData = {
export type CreateWebsocketTriggerData = {
/**
* new websocket trigger
*/
requestBody: NewWebsocketTrigger;
workspace: string;
};
export type UsedResponse = (boolean);
export type CreateWebsocketTriggerResponse = (string);
export type UpdateWebsocketTriggerData = {
path: string;
/**
* updated trigger
*/
requestBody: EditWebsocketTrigger;
workspace: string;
};
export type UpdateWebsocketTriggerResponse = (string);
export type DeleteWebsocketTriggerData = {
path: string;
workspace: string;
};
export type DeleteWebsocketTriggerResponse = (string);
export type GetWebsocketTriggerData = {
path: string;
workspace: string;
};
export type GetWebsocketTriggerResponse = (WebsocketTrigger);
export type ListWebsocketTriggersData = {
isFlow?: boolean;
/**
* which page to return (start at 1, default 1)
*/
page?: number;
/**
* filter by path
*/
path?: string;
pathStart?: string;
/**
* number of items to return for a given page (default 30, max 100)
*/
perPage?: number;
workspace: string;
};
export type ListWebsocketTriggersResponse = (Array<WebsocketTrigger>);
export type ExistsWebsocketTriggerData = {
path: string;
workspace: string;
};
export type ExistsWebsocketTriggerResponse = (boolean);
export type SetWebsocketTriggerEnabledData = {
path: string;
/**
* updated websocket trigger enable
*/
requestBody: {
enabled: boolean;
};
workspace: string;
};
export type SetWebsocketTriggerEnabledResponse = (string);
export type ListInstanceGroupsResponse = (Array<InstanceGroup>);
@@ -5201,6 +5572,10 @@ export type GetQueueMetricsResponse = (Array<{
}>;
}>);
export type GetCountsOfJobsWaitingPerTagResponse = ({
[key: string]: (number);
});
export type ListWorkerGroupsResponse = (Array<{
name: string;
config: unknown;
@@ -5230,8 +5605,14 @@ export type DeleteConfigResponse = (string);
export type ListConfigsResponse = (Array<Config>);
export type ListAutoscalingEventsData = {
workerGroup: string;
};
export type ListAutoscalingEventsResponse = (Array<AutoscalingEvent>);
export type GetGranularAclsData = {
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger';
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger';
path: string;
workspace: string;
};
@@ -5241,7 +5622,7 @@ export type GetGranularAclsResponse = ({
});
export type AddGranularAclsData = {
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger';
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger';
path: string;
/**
* acl to add
@@ -5256,7 +5637,7 @@ export type AddGranularAclsData = {
export type AddGranularAclsResponse = (string);
export type RemoveGranularAclsData = {
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger';
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger';
path: string;
/**
* acl to add
@@ -5569,6 +5950,8 @@ export type MoveS3FileData = {
export type MoveS3FileResponse = (unknown);
export type FileUploadData = {
contentDisposition?: string;
contentType?: string;
fileExtension?: string;
fileKey?: string;
/**
@@ -5817,4 +6200,44 @@ export type SearchJobsIndexResponse = ({
* the jobs that matched the query
*/
hits?: Array<JobSearchHit>;
});
export type SearchLogsIndexData = {
hostname: string;
maxTs?: string;
minTs?: string;
mode: string;
searchQuery: string;
workerGroup?: string;
};
export type SearchLogsIndexResponse = ({
/**
* a list of the terms that couldn't be parsed (and thus ignored)
*/
query_parse_errors?: Array<(string)>;
/**
* log files that matched the query
*/
hits?: Array<LogSearchHit>;
});
export type CountSearchLogsIndexData = {
hosts: string;
maxTs?: string;
minTs?: string;
searchQuery: string;
};
export type CountSearchLogsIndexResponse = ({
/**
* a list of the terms that couldn't be parsed (and thus ignored)
*/
query_parse_errors?: Array<(string)>;
/**
* count of log lines that matched the query per hostname
*/
count_per_host?: {
[key: string]: unknown;
};
});

View File

@@ -60,7 +60,7 @@ export {
// }
// });
export const VERSION = "1.424.0";
export const VERSION = "1.425.1";
const command = new Command()
.name("wmill")

View File

@@ -4,10 +4,10 @@ import { colors } from "./deps.ts";
import { yamlParseFile } from "./deps.ts";
import { log } from "./deps.ts";
import { compareInstanceObjects, InstanceSyncOptions } from "./instance.ts";
import { isSuperset, GlobalOptions } from "./types.ts";
import { isSuperset } from "./types.ts";
import { deepEqual } from "./utils.ts";
import * as wmill from "./gen/services.gen.ts";
import { Config, GlobalSetting } from "./gen/types.gen.ts";
import { AiResource, Config, GlobalSetting } from "./gen/types.gen.ts";
import { removeWorkerPrefix } from "./worker_groups.ts";
import process from "node:process";
@@ -24,7 +24,7 @@ export interface SimplifiedSettings {
error_handler?: string;
error_handler_extra_args?: any;
error_handler_muted_on_cancel?: boolean;
openai_resource_path?: string;
ai_resource?: AiResource;
code_completion_enabled: boolean;
large_file_storage?: any;
git_sync?: any;
@@ -80,7 +80,7 @@ export async function pushWorkspaceSettings(
error_handler_extra_args: remoteSettings.error_handler_extra_args,
error_handler_muted_on_cancel:
remoteSettings.error_handler_muted_on_cancel,
openai_resource_path: remoteSettings.openai_resource_path,
ai_resource: remoteSettings.ai_resource,
code_completion_enabled: remoteSettings.code_completion_enabled,
large_file_storage: remoteSettings.large_file_storage,
git_sync: remoteSettings.git_sync,
@@ -153,14 +153,14 @@ export async function pushWorkspaceSettings(
}
}
if (
localSettings.openai_resource_path !== settings.openai_resource_path ||
localSettings.ai_resource !== settings.ai_resource ||
localSettings.code_completion_enabled !== settings.code_completion_enabled
) {
log.debug(`Updating openai settings...`);
await wmill.editCopilotConfig({
workspace,
requestBody: {
openai_resource_path: localSettings.openai_resource_path,
ai_resource: localSettings.ai_resource,
code_completion_enabled: localSettings.code_completion_enabled,
},
});

View File

@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.424.0",
"version": "1.425.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.424.0",
"version": "1.425.1",
"license": "AGPL-3.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.32.1",

View File

@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.424.0",
"version": "1.425.1",
"scripts": {
"dev": "vite dev",
"build": "vite build",

View File

@@ -8,6 +8,7 @@
cleanValueProperties,
orderedJsonStringify,
orderedYamlStringify,
replaceFalseWithUndefined,
type Value
} from '$lib/utils'
import type { Script } from '$lib/gen'
@@ -54,8 +55,9 @@
diffViewer.closeDrawer()
}
function prepareDiff(data: Value) {
const metadata = structuredClone(cleanValueProperties(data))
const metadata = structuredClone(cleanValueProperties(replaceFalseWithUndefined(data)))
const content = metadata['content']
if (metadata['content'] !== undefined) {
metadata['content'] = 'check content diff'

View File

@@ -25,6 +25,7 @@
encodeState,
formatCron,
orderedJsonStringify,
replaceFalseWithUndefined,
sleep,
type Value
} from '$lib/utils'
@@ -294,12 +295,21 @@
// We need it for diff
await syncWithDeployed()
// Handle through confirmation modal
confirmCallback = async () => {
if (
deployedValue &&
$flowStore &&
orderedJsonStringify(deployedValue) ===
orderedJsonStringify(replaceFalseWithUndefined({ ...$flowStore, path: $pathStore }))
) {
await saveFlow(deploymentMsg)
} else {
// Handle through confirmation modal
confirmCallback = async () => {
await saveFlow(deploymentMsg)
}
// Open confirmation modal
open = true
}
// Open confirmation modal
open = true
}
}
async function syncWithDeployed() {
@@ -308,18 +318,12 @@
path: initialPath,
withStarredInfo: true
})
deployedValue = {
deployedValue = replaceFalseWithUndefined({
...flow,
starred: undefined,
id: undefined,
edited_at: undefined,
edited_by: undefined,
workspace_id: undefined,
archived: undefined,
same_worker: undefined,
visible_to_runner_only: undefined,
ws_error_handler_muted: undefined
}
workspace_id: undefined
})
deployedBy = flow.edited_by
}

View File

@@ -18,6 +18,7 @@
encodeState,
formatCron,
orderedJsonStringify,
replaceFalseWithUndefined,
type Value
} from '$lib/utils'
import Path from './Path.svelte'
@@ -278,17 +279,28 @@
// Fetch entire script, since we need it to show Diff
await syncWithDeployed()
// Handle through confirmation modal
confirmCallback = async () => {
open = false
if (actual_parent_hash) {
await editScript(stay, actual_parent_hash, deployMsg)
} else {
sendUserToast('Could not fetch latest version of the script', true)
if (
deployedValue &&
script &&
orderedJsonStringify({ ...deployedValue, hash: undefined }) ===
orderedJsonStringify(
replaceFalseWithUndefined({ ...script, hash: undefined, parent_hash: undefined })
)
) {
await editScript(stay, actual_parent_hash, deployMsg)
} else {
// Handle through confirmation modal
confirmCallback = async () => {
open = false
if (actual_parent_hash) {
await editScript(stay, actual_parent_hash, deployMsg)
} else {
sendUserToast('Could not fetch latest version of the script', true)
}
}
// Open confirmation modal
open = true
}
// Open confirmation modal
open = true
}
}
@@ -299,20 +311,16 @@
withStarredInfo: true
})
deployedValue = {
deployedValue = replaceFalseWithUndefined({
...latestScript,
starred: undefined,
workspace_id: undefined,
archived: undefined,
created_at: undefined,
created_by: undefined,
deleted: undefined,
extra_perms: undefined,
is_template: undefined,
lock: undefined,
lock_error_logs: undefined,
parent_hashes: undefined
}
})
deployedBy = latestScript.created_by
}
@@ -691,8 +699,11 @@
</Label>
<Label label="Path">
<svelte:fragment slot="header">
<Tooltip documentationLink="https://www.windmill.dev/docs/core_concepts/roles_and_permissions#path">
The unique identifier of the script in the workspace that defines permissions
<Tooltip
documentationLink="https://www.windmill.dev/docs/core_concepts/roles_and_permissions#path"
>
The unique identifier of the script in the workspace that defines
permissions
</Tooltip>
</svelte:fragment>
<Path

View File

@@ -16,10 +16,13 @@
import { offset, flip, shift } from 'svelte-floating-ui/dom'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte'
import type { ObjectOption } from '../../../multiselect/types'
import { parseConfigOptions } from './utils'
import { deepEqual } from 'fast-equals'
export let id: string
export let configuration: RichConfigurations
export let customCss: ComponentCustomCSS<'multiselectcomponent'> | undefined = undefined
export let id: string
export let render: boolean
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
@@ -30,55 +33,33 @@
const { app, worldStore, selectedComponent, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
let items: string[]
const resolvedConfig = initConfig(
components['multiselectcomponent'].initialData.configuration,
components['multiselectcomponentv2'].initialData.configuration,
configuration
)
const outputs = initOutput($worldStore, id, {
result: [] as string[]
})
let value: string[] | undefined = [...new Set(outputs?.result.peak())] as string[]
$componentControl[id] = {
setValue(nvalue: string[]) {
value = [...new Set(nvalue)]
outputs?.result.set([...(value ?? [])])
}
}
$: resolvedConfig.items && handleItems()
function handleItems() {
if (Array.isArray(resolvedConfig.items)) {
items = resolvedConfig.items?.map((label) => {
return typeof label === 'string' ? label : `NOT_STRING`
})
}
}
$: resolvedConfig.defaultItems && handleDefaultItems()
function handleDefaultItems() {
if (Array.isArray(resolvedConfig.defaultItems)) {
const nvalue = resolvedConfig.defaultItems?.map((label) => {
return typeof label === 'string' ? label : `NOT_STRING`
})
value = [...new Set(nvalue)]
outputs?.result.set([...(value ?? [])])
}
}
const outputs = initOutput($worldStore, id, { result: [] as any[] })
let options: ObjectOption[] = []
let selectedOptions: ObjectOption[] = [...new Set(findOptionsByValue(outputs?.result.peak()))]
let css = initCss($app.css?.multiselectcomponent, customCss)
let outerDiv: HTMLDivElement | undefined = undefined
let portalRef: HTMLDivElement | undefined = undefined
let w = 0
let open: boolean = false
function setOuterDivStyle(outerDiv: HTMLDivElement, portalRef: HTMLDivElement, style: string) {
outerDiv.setAttribute('style', style)
// find ul in portalRef and set style
const ul = portalRef.querySelector('ul')
ul?.setAttribute('style', extractCustomProperties(style))
$: if (resolvedConfig.items) {
options = parseConfigOptions(resolvedConfig.items)
}
$componentControl[id] = {
setValue(newValues: any[]) {
selectOptionsByValue(newValues)
}
}
$: if (resolvedConfig.defaultItems) {
selectOptionsByValue(resolvedConfig.defaultItems)
}
$: outerDiv &&
@@ -86,8 +67,56 @@
css?.multiselect?.style &&
setOuterDivStyle(outerDiv, portalRef, css?.multiselect?.style)
let outerDiv: HTMLDivElement | undefined = undefined
let portalRef: HTMLDivElement | undefined = undefined
$: if (render && portalRef && outerDiv && options?.length > 0) {
tick().then(() => {
moveOptionsToPortal()
})
}
function findOptionsByValue(values: any[]) {
if (!Array.isArray(values)) {
return []
}
return options?.filter((item) => {
let itemValue = item.value
try {
if (typeof itemValue == 'string') {
itemValue = JSON.parse(itemValue)
}
} catch (_) {}
return values.some((value) => deepEqual(itemValue, value))
}) as ObjectOption[]
}
function selectOptionsByValue(values: any[]) {
if (!Array.isArray(values)) {
outputs?.result.set([])
}
selectedOptions = findOptionsByValue(values)
outputs?.result.set([...toValues(selectedOptions)])
}
function toValues(options: ObjectOption[] | undefined) {
if (!options || !Array.isArray(options)) {
return []
}
return options.map((o) => {
let val = o.value
try {
if (typeof val === 'string') val = JSON.parse(val)
} catch (_) {}
return val
})
}
function setOuterDivStyle(outerDiv: HTMLDivElement, portalRef: HTMLDivElement, style: string) {
outerDiv.setAttribute('style', style)
// find ul in portalRef and set style
const ul = portalRef.querySelector('ul')
ul?.setAttribute('style', extractCustomProperties(style))
}
function moveOptionsToPortal() {
// Find ul element with class 'options' within the outerDiv
@@ -98,14 +127,6 @@
portalRef?.appendChild(ul)
}
}
$: if (render && portalRef && outerDiv && items?.length > 0) {
tick().then(() => {
moveOptionsToPortal()
})
}
let w = 0
let open: boolean = false
</script>
{#each Object.keys(components['multiselectcomponent'].initialData.configuration) as key (key)}
@@ -128,7 +149,6 @@
{/each}
<InitializeComponent {id} />
<AlignWrapper {render} hFull {verticalAlignment}>
<div
class="w-full app-editor-input"
@@ -143,7 +163,7 @@
use:floatingRef
bind:clientWidth={w}
>
{#if !value || Array.isArray(value)}
{#if !selectedOptions || Array.isArray(selectedOptions)}
<MultiSelect
bind:outerDiv
outerDivClass={`${resolvedConfig.allowOverflow ? '' : 'h-full'}`}
@@ -151,15 +171,19 @@
--sms-border={'none'}
--sms-min-height={'32px'}
--sms-focus-border={'none'}
bind:selected={value}
options={items}
bind:selected={selectedOptions}
{options}
placeholder={resolvedConfig.placeholder}
allowUserOptions={resolvedConfig.create}
key={(option) => {
if (typeof option == 'string' || typeof option == 'number') return option
return option.value
}}
on:change={(event) => {
if (event?.detail?.type === 'removeAll') {
outputs?.result.set([])
} else {
outputs?.result.set([...(value ?? [])])
outputs?.result.set([...toValues(selectedOptions)])
}
}}
on:open={() => {
@@ -181,9 +205,10 @@
e.target?.['parentElement']?.dispatchEvent(newe)
}}
>
{option}
{@html option.label === '' ? `&nbsp;` : option.label}
</div>
</MultiSelect>
<Portal name="app-multiselect-v2">
<div use:floatingContent class="z5000" hidden={!open}>
<!-- svelte-ignore a11y-click-events-have-key-events -->
@@ -197,7 +222,7 @@
</div>
</Portal>
{:else}
Value {value} is not an array
Value {selectedOptions} is not an array
{/if}
</div>
</AlignWrapper>

View File

@@ -0,0 +1,48 @@
import {
type ObjectOption,
isObjectOptionArray,
isStringArray
} from '$lib/components/multiselect/types'
import type { StaticAppInput } from '../../inputType'
export function parseConfigOptions(resolvedConfigItems: StaticAppInput) {
if (!resolvedConfigItems) {
return []
}
if (isObjectOptionArray(resolvedConfigItems)) {
return parseLabeledItems(resolvedConfigItems)
} else if (isStringArray(resolvedConfigItems)) {
return parseStringItems(resolvedConfigItems)
}
return []
}
export function parseLabeledItems(resolvedConfigItems: ObjectOption[]) {
return resolvedConfigItems?.map((item: ObjectOption) => {
if (!item || typeof item !== 'object') {
console.error(
'When labeled, MultiSelect component items should be an array of { label: string, value: string }.'
)
return {
label: 'not object',
value: 'not object'
}
}
return {
label: item?.label ?? 'undefined',
value:
typeof item?.value === 'object' ? JSON.stringify(item.value) : item?.value ?? 'undefined'
}
})
}
export function parseStringItems(resolvedConfigItems: string[]): ObjectOption[] {
return resolvedConfigItems?.map((option: string) => {
if (option === null || option === undefined || typeof option !== 'string') {
console.error('When not labeled, MultiSelect component items should be an array of strings.')
return { label: 'not string', value: 'not string' }
}
if (option === '') {
return { label: 'empty string', value: 'empty string' }
}
return { label: option, value: option }
})
}

View File

@@ -41,7 +41,8 @@
copyToClipboard,
truncateRev,
orderedJsonStringify,
type Value
type Value,
replaceFalseWithUndefined
} from '../../../utils'
import type {
AppInput,
@@ -422,13 +423,27 @@
// We need it to show diff
// Handle through confirmation modal
await syncWithDeployed()
confirmCallback = async () => {
open = false
if (
deployedValue &&
savedApp &&
$app &&
orderedJsonStringify(deployedValue) ===
orderedJsonStringify(replaceFalseWithUndefined({
summary: $summary,
value: $app,
path: newPath || savedApp.draft?.path || savedApp.path,
policy
}))
) {
await updateApp(npath)
} else {
confirmCallback = async () => {
open = false
await updateApp(npath)
}
// Open confirmation modal
open = true
}
// Open confirmation modal
open = true
}
}
@@ -442,15 +457,14 @@
deployedBy = deployedApp.created_by
// Strip off extra information
deployedValue = {
deployedValue = replaceFalseWithUndefined({
...deployedApp,
starred: undefined,
id: undefined,
created_at: undefined,
created_by: undefined,
versions: undefined,
extra_perms: undefined //
}
extra_perms: undefined
})
}
async function updateApp(npath: string) {

View File

@@ -2079,7 +2079,8 @@ This is a paragraph.
value: [
{ value: 'foo', label: 'Foo' },
{ value: 'bar', label: 'Bar' }
]
],
hasLabeledMode: true
} as StaticAppInput,
create: {
type: 'static',
@@ -2199,14 +2200,15 @@ This is a paragraph.
items: {
type: 'static',
fieldType: 'array',
subFieldType: 'text',
value: ['Foo', 'Bar']
subFieldType: 'simplestringselect',
value: ['Foo', 'Bar'],
hasLabeledMode: true
} as StaticAppInput,
defaultItems: {
type: 'static',
fieldType: 'array',
subFieldType: 'text',
value: []
subFieldType: 'simplestringselect',
value: undefined
} as StaticAppInput,
placeholder: {
type: 'static',

View File

@@ -9,15 +9,30 @@
import Toggle from '$lib/components/Toggle.svelte'
import QuickAddColumn from './QuickAddColumn.svelte'
import RefreshDatabaseStudioTable from './RefreshDatabaseStudioTable.svelte'
import { isObjectOptionArray, isStringArray } from '$lib/components/multiselect/types'
export let componentInput: StaticInput<any[]> & { loading?: boolean }
export let componentInput: StaticInput<any[]> & {
loading?: boolean
expr?: string
isLabeled?: boolean
}
export let subFieldType: InputType | undefined = undefined
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
export let id: string | undefined
export let hasLabeledMode: boolean = false
const dispatch = createEventDispatcher()
const flipDurationMs = 200
let items = getItems(componentInput)
let raw: boolean = false
let labeled: boolean = subFieldType === 'labeledselect'
$: subFieldType === 'db-explorer' && clearTableOnComponentReset(componentInput?.value)
$: labeled !== undefined && handleLabeledChange()
$: items !== undefined && handleItemsChange()
function addElementByType() {
if (!Array.isArray(componentInput.value)) {
componentInput.value = []
@@ -32,12 +47,13 @@
} else if (subFieldType === 'object') {
value.push({})
} else if (subFieldType === 'labeledresource' || subFieldType === 'labeledselect') {
value.push({ value: 'value', label: 'label' })
value.push({ label: 'label', value: 'value' })
} else if (subFieldType === 'tab-select') {
value.push({ id: '', index: 0 })
} else if (
subFieldType === 'text' ||
subFieldType === 'textarea' ||
subFieldType === 'simplestringselect' ||
// TODO: Add support for these types
subFieldType === 'date' ||
subFieldType === 'time' ||
@@ -193,8 +209,6 @@
componentInput.value = reorderedValues
}
let items = getItems(componentInput)
function getItems(componentInput: StaticInput<any[]> & { loading?: boolean }) {
return (Array.isArray(componentInput.value) ? componentInput.value : [])
.filter((x) => x != undefined)
@@ -209,15 +223,30 @@
}
}
$: subFieldType === 'db-explorer' && clearTableOnComponentReset(componentInput?.value)
$: items != undefined && handleItemsChange()
function handleItemsChange() {
componentInput.value = items.map((item) => item.value).filter((item) => item != undefined)
const newComponentInput = { ...componentInput }
newComponentInput.value = items.map((item) => item.value).filter((item) => item != undefined)
newComponentInput.expr = JSON.stringify(newComponentInput.value)
newComponentInput.isLabeled = labeled
dispatch('componentInputChange', newComponentInput)
}
/** Transforms items string[] to ObjectOption] and vice versa. Mutating items will fire handleItemsChange. */
function handleLabeledChange() {
if (labeled && isStringArray(items.map((item) => item.value))) {
for (let i = 0; i < items.length; i++) {
const itemValue = items[i].value
items[i].value = { label: itemValue, value: itemValue }
}
} else if (!labeled && isObjectOptionArray(items.map((item) => item.value))) {
for (let i = 0; i < items.length; i++) {
const itemValue = items[i].value
items[i].value = itemValue.label
}
}
}
let raw: boolean = false
// let mounted = false
// $: if (componentInput.value && mounted) {
@@ -243,7 +272,6 @@
{#if Array.isArray(items) && componentInput.value}
<div class="flex flex-row items-center justify-between">
<div class="text-xs text-tertiary font-semibold">{pluralize(items.length, 'item')}</div>
{#if subFieldType === 'ag-grid' || subFieldType === 'table-column'}
<Toggle
options={{
@@ -253,6 +281,16 @@
bind:checked={raw}
/>
{/if}
{#if hasLabeledMode}
<Toggle
options={{
right: 'w/ label'
}}
size="xs"
bind:checked={labeled}
on:change={handleLabeledChange}
/>
{/if}
</div>
<section
use:dragHandleZone={{
@@ -271,7 +309,7 @@
<div class="grow min-w-0">
<SubTypeEditor
{id}
subFieldType={raw ? 'object' : subFieldType}
subFieldType={raw ? 'object' : labeled ? 'labeledselect' : subFieldType}
bind:componentInput
bind:value={item.value}
on:remove={() => deleteElementByType(index)}

View File

@@ -43,6 +43,7 @@
export let documentationLink: string | undefined = undefined
export let markdownTooltip: string | undefined = undefined
export let securedContext = false
export let hasLabeledMode = false
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
@@ -171,6 +172,7 @@
{selectOptions}
{format}
{placeholder}
{hasLabeledMode}
bind:componentInput
/>
</div>

View File

@@ -81,6 +81,7 @@
{recomputeOnInputChanged}
{showOnDemandOnlyToggle}
{securedContext}
hasLabeledMode={meta?.['hasLabeledMode']}
/>
{#if deletable}
<div class="flex flex-row-reverse -mt-4">

View File

@@ -30,6 +30,9 @@
editor?.setCode(code)
}
let fullscreen = false
let focus = false
$: extraLib =
componentInput?.expr && $worldStore
? buildExtraLib($worldStore?.outputsById ?? {}, acceptSelf ? '' : id, $state, false)
@@ -52,9 +55,6 @@
inferDeps(code, $worldStore.outputsById, componentInput, app)
}
}
let fullscreen = false
let focus = false
</script>
{#if componentInput?.type === 'evalv2'}

View File

@@ -3,7 +3,7 @@
import ArrayStaticInputEditor from '../ArrayStaticInputEditor.svelte'
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
import JsonEditor from './JsonEditor.svelte'
import { getContext } from 'svelte'
import { getContext, onMount } from 'svelte'
import type { AppViewerContext } from '$lib/components/apps/types'
import IconSelectInput from './IconSelectInput.svelte'
import ColorInput from './ColorInput.svelte'
@@ -24,17 +24,49 @@
import EditableSchemaDrawer from '$lib/components/schema/EditableSchemaDrawer.svelte'
import AppPicker from '$lib/components/wizards/AppPicker.svelte'
export let componentInput: StaticInput<any> | undefined
export let componentInput: (StaticInput<any> & { isLabeled?: boolean }) | undefined
export let fieldType: InputType | undefined = undefined
export let subFieldType: InputType | undefined = undefined
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
export let placeholder: string | undefined = undefined
export let format: string | undefined = undefined
export let id: string | undefined
export let hasLabeledMode: boolean = false
const { onchange } = getContext<AppViewerContext>('AppViewerContext')
$: componentInput && onchange?.()
onMount(() => {
/** preserve the `labeled` toggling when switching from eval to static */
if (componentInput !== undefined) {
handleComponentInputChange(componentInput)
}
})
/**
* Every components: switching from `static` to `eval` preserves the user data
*
* Multiselect: toggling `labeled` needs to mutate input value and input fieldType.
* */
function handleComponentInputChange(newComponentInput: StaticInput<any>) {
componentInput = { ...componentInput, ...newComponentInput }
if (componentInput?.isLabeled) {
if (fieldType === 'array' && subFieldType === 'simplestringselect') {
subFieldType = 'labeledselect'
}
if (fieldType === 'simplestringselect') {
fieldType = 'labeledselect'
}
} else if (componentInput?.isLabeled === false) {
if (fieldType === 'array' && subFieldType === 'labeledselect') {
subFieldType = 'simplestringselect'
}
if (fieldType === 'labeledselect') {
fieldType = 'simplestringselect'
}
}
}
</script>
{#key subFieldType}
@@ -133,7 +165,7 @@
{/if}
{:else if fieldType === 'color'}
<ColorInput bind:value={componentInput.value} />
{:else if fieldType === 'object' || fieldType == 'labeledselect'}
{:else if fieldType === 'object' || fieldType == 'labeledselect' || fieldType === 'simplestringselect'}
{#if format?.startsWith('resource-') && (componentInput.value == undefined || typeof componentInput.value == 'string')}
<ResourcePicker
initialValue={componentInput.value?.split('$res:')?.[1] || ''}
@@ -162,7 +194,14 @@
</div>
{/if}
{:else if fieldType === 'array'}
<ArrayStaticInputEditor {id} {subFieldType} bind:componentInput on:deleteArrayItem />
<ArrayStaticInputEditor
{id}
{subFieldType}
bind:componentInput
on:deleteArrayItem
on:componentInputChange={(e) => handleComponentInputChange(e.detail)}
{hasLabeledMode}
/>
{:else if fieldType === 'schema'}
<div class="w-full">
<EditableSchemaDrawer bind:schema={componentInput.value} />

View File

@@ -20,6 +20,7 @@ export type InputType =
| 'any'
| 'labeledresource'
| 'labeledselect'
| 'simplestringselect'
| 'tab-select'
| 'schema'
| 'ag-grid'
@@ -175,6 +176,7 @@ type InputConfiguration<T extends InputType, V extends InputType> = {
noStatic?: boolean
onDemandOnly?: boolean
hideRefreshButton?: boolean
isLabeled?: boolean
}
export type StaticOptions = {
@@ -207,6 +209,7 @@ export type AppInput =
| (AppInputSpec<'array', string[], 'select'> & StaticOptions)
| AppInputSpec<'array', object[], 'labeledresource'>
| AppInputSpec<'array', object[], 'labeledselect'>
| AppInputSpec<'array', string[], 'simplestringselect'>
| AppInputSpec<'labeledselect', object>
| AppInputSpec<'labeledresource', object>
| AppInputSpec<'array', object[], 'tab-select'>

View File

@@ -4,7 +4,12 @@
import { goto as gotoUrl } from '$app/navigation'
import Button from '../button/Button.svelte'
import type DiffDrawer from '$lib/components/DiffDrawer.svelte'
import { cleanValueProperties, orderedJsonStringify, type Value } from '$lib/utils'
import {
cleanValueProperties,
orderedJsonStringify,
replaceFalseWithUndefined,
type Value
} from '$lib/utils'
import { tick } from 'svelte'
import { page } from '$app/stores'
@@ -36,7 +41,10 @@
path: undefined
})
const current = cleanValueProperties({ ...(modifiedValue ?? {}), path: undefined })
if (orderedJsonStringify(draftOrDeployed) === orderedJsonStringify(current)) {
if (
orderedJsonStringify(replaceFalseWithUndefined(draftOrDeployed)) ===
orderedJsonStringify(replaceFalseWithUndefined(current))
) {
bypassBeforeNavigate = true
gotoUrl(goingTo)
} else {

View File

@@ -62,7 +62,7 @@
{:else if $selectedId === 'Input'}
<FlowInput {noEditor} disabled={disabledFlowInputs} />
{:else if $selectedId === 'Result'}
<p class="p-4 text-secondary">Nothing to show about the result node. Happy flow building!</p>
<p class="p-4 text-secondary">The result of the flow will be the result of the last node.</p>
{:else if $selectedId === 'constants'}
<FlowConstants {noEditor} />
{:else if $selectedId === 'failure'}

View File

@@ -45,3 +45,30 @@ export type MultiSelectEvents = {
touchmove: TouchEvent
touchstart: TouchEvent
}
export function isObjectOption(item: any): item is ObjectOption {
if (!item || item !== Object(item)) {
return false
}
if (item.label === undefined || item.label === null) {
return false
}
if (typeof item.label !== 'string' && typeof item.label !== 'number') {
return false
}
return true
}
export function isObjectOptionArray(arr: any): arr is ObjectOption[] {
if (!arr || !Array.isArray(arr)) {
return false
}
return arr.every((item) => isObjectOption(item))
}
export function isStringArray(arr: any): arr is string[] {
if (!arr || !Array.isArray(arr)) {
return false
}
return arr.every((item) => typeof item === 'string')
}

View File

@@ -799,6 +799,30 @@ export type Value = {
[key: string]: any
}
export function replaceFalseWithUndefined(obj: any) {
return replaceFalseWithUndefinedRec(structuredClone(obj))
}
function replaceFalseWithUndefinedRec(obj: any) {
// Check if the input is an object and not null
if (obj !== null && typeof obj === 'object') {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// If the value is false, replace it with undefined
if (obj[key] === false) {
// delete obj[key];
obj[key] = undefined;
} else {
// If the value is an object, call the function recursively
replaceFalseWithUndefinedRec(obj[key]);
}
}
}
}
return obj
}
export function cleanValueProperties(obj: Value) {
if (typeof obj !== 'object') {
return obj

View File

@@ -129,7 +129,7 @@
...structuredClone(flowWithDraft.draft),
path: flowWithDraft.draft.path ?? flowWithDraft.path // backward compatibility for old drafts missing path
}
: undefined
: undefined
} as Flow & {
draft?: Flow
}

View File

@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.424.0"
wmill_pg = ">=1.424.0"
wmill = ">=1.425.1"
wmill_pg = ">=1.425.1"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.424.0
version: 1.425.1
title: OpenFlow Spec
contact:
name: Ruben Fiszel

View File

@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.424.0'
ModuleVersion = '1.425.1'
# Supported PSEditions
# CompatiblePSEditions = @()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.424.0"
version = "1.425.1"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.424.0"
version = "1.425.1"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.424.0",
"version": "1.425.1",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]

View File

@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.424.0",
"version": "1.425.1",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {

View File

@@ -1 +1 @@
1.424.0
1.425.1