Compare commits

..

17 Commits

Author SHA1 Message Date
Faton Ramadani
072f1777b5 fix(frontend): timezone fix 2023-09-28 15:30:58 +02:00
Faton Ramadani
55b7f982c2 fix(frontend): Properly handle click (#2351)
* fix(frontend): Properly handle click

* fix(frontend): Properly handle click
2023-09-28 05:07:26 -07:00
Ruben Fiszel
2c401305bf chore(main): release 1.178.1 (#2350)
* chore(main): release 1.178.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-09-28 05:04:27 +02:00
Ruben Fiszel
a2594a7731 improve license key check 2023-09-28 04:58:44 +02:00
Ruben Fiszel
035bad5268 fix: improve license key check 2023-09-28 04:28:52 +02:00
Ruben Fiszel
4db788b845 chore(main): release 1.178.0 (#2349)
* chore(main): release 1.178.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-09-28 03:13:33 +02:00
Ruben Fiszel
44926eb39e fix sqlx 2023-09-28 02:58:56 +02:00
Faton Ramadani
20e0427a13 feat(frontend): add app groups management (#2347)
* feat(frontend): add app groups management

* feat(frontend): add app groups management

* app groups

* app groups

* app groups

---------

Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2023-09-28 01:18:35 +02:00
Ruben Fiszel
c384387419 UX nits 2023-09-27 23:51:30 +02:00
Faton Ramadani
3de6d446f2 feat(frontend): add AppDrawer controls (#2339)
* feat(frontend): add AppDrawer controls

* feat(frontend): add AppDrawer controls

* Update utils.ts

* fix app drawer cc

* fix app drawer cc

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2023-09-27 23:22:54 +02:00
Faton Ramadani
6fcd72c794 feat(frontend): Switch to component list when deleting a component (#2346)
* feat(frontend): Switch to component list when deleting a component

* feat(frontend): remove console.log
2023-09-27 10:13:57 -07:00
HugoCasa
f5bed95ab1 fix: add env tags to default worker group (#2348) 2023-09-27 10:11:21 -07:00
Ruben Fiszel
4219377bfc chore(main): release 1.177.1 (#2342)
* chore(main): release 1.177.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-09-26 20:43:29 +02:00
HugoCasa
ff1b4df1ac fix: disable schema awareness when object resource (#2343) 2023-09-26 20:12:34 +02:00
Ruben Fiszel
dc44b0841a fix: improve list component handling of non array data 2023-09-26 18:41:28 +02:00
Faton Ramadani
18cf8faec1 fix(frontend): fix modal closing issues (#2340) 2023-09-26 17:23:14 +02:00
Faton Ramadani
2e8f2ec724 fix(frontend): fix overflow (#2341)
* fix(frontend): fix overflow

* fix(frontend): fix overflow
2023-09-26 17:22:52 +02:00
67 changed files with 936 additions and 260 deletions

View File

@@ -1,6 +1,36 @@
# Changelog
## [1.178.1](https://github.com/windmill-labs/windmill/compare/v1.178.0...v1.178.1) (2023-09-28)
### Bug Fixes
* improve license key check ([035bad5](https://github.com/windmill-labs/windmill/commit/035bad5268d182af3f30915b5356defd7f6ccbc0))
## [1.178.0](https://github.com/windmill-labs/windmill/compare/v1.177.1...v1.178.0) (2023-09-28)
### Features
* **frontend:** add app groups management ([#2347](https://github.com/windmill-labs/windmill/issues/2347)) ([20e0427](https://github.com/windmill-labs/windmill/commit/20e0427a1303c1c32f41b198cd2d0f7f28b5bd32))
* **frontend:** add AppDrawer controls ([#2339](https://github.com/windmill-labs/windmill/issues/2339)) ([3de6d44](https://github.com/windmill-labs/windmill/commit/3de6d446f281dcaac288deee19342a08e0ccf9af))
* **frontend:** Switch to component list when deleting a component ([#2346](https://github.com/windmill-labs/windmill/issues/2346)) ([6fcd72c](https://github.com/windmill-labs/windmill/commit/6fcd72c79453dd9d60ca869cd9996cc0c25971fa))
### Bug Fixes
* add env tags to default worker group ([#2348](https://github.com/windmill-labs/windmill/issues/2348)) ([f5bed95](https://github.com/windmill-labs/windmill/commit/f5bed95ab15bc397f822b06816c43b4b13a84af3))
## [1.177.1](https://github.com/windmill-labs/windmill/compare/v1.177.0...v1.177.1) (2023-09-26)
### Bug Fixes
* **frontend:** fix modal closing issues ([#2340](https://github.com/windmill-labs/windmill/issues/2340)) ([18cf8fa](https://github.com/windmill-labs/windmill/commit/18cf8faec16d496e4b327505b682459ed518a5b4))
* **frontend:** fix overflow ([#2341](https://github.com/windmill-labs/windmill/issues/2341)) ([2e8f2ec](https://github.com/windmill-labs/windmill/commit/2e8f2ec724f6802170121f4f8aa73b697a39c9ee))
* improve list component handling of non array data ([dc44b08](https://github.com/windmill-labs/windmill/commit/dc44b0841af17227160b9d56ec446e6646a8ab0d))
## [1.177.0](https://github.com/windmill-labs/windmill/compare/v1.176.0...v1.177.0) (2023-09-26)

View File

@@ -37,6 +37,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT script_path FROM completed_job WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "script_path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
true
]
},
"hash": "16be0560028361d46bf3b842a5fa07472994d8942c684f5b75339fe71ea23cdd"
}

View File

@@ -67,6 +67,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -28,6 +28,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -42,6 +42,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -42,6 +42,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -46,6 +46,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "6e6d781fe63602541f7cb4806d5ddde0792e1861ea8761b4875677c3d0594a86"
}

View File

@@ -60,6 +60,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -42,6 +42,7 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT hash FROM script WHERE path = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "hash",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false
]
},
"hash": "ef6795d93423f98eea82eb18e6332580dc7f7a9e5a67026f8c0b3077f371fc62"
}

121
backend/Cargo.lock generated
View File

@@ -321,7 +321,7 @@ dependencies = [
"serde",
"serde-aux",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"thiserror",
"url",
]
@@ -378,7 +378,7 @@ dependencies = [
"serde",
"serde_json",
"serde_qs 0.9.2",
"sha2 0.10.7",
"sha2 0.10.8",
"smart-default",
"smol_str",
"thiserror",
@@ -1891,13 +1891,12 @@ dependencies = [
[[package]]
name = "flume"
version = "0.10.14"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
"futures-core",
"futures-sink",
"pin-project",
"spin 0.9.8",
]
@@ -2574,9 +2573,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e"
dependencies = [
"equivalent",
"hashbrown 0.14.0",
@@ -3221,7 +3220,7 @@ dependencies = [
"serde",
"serde_json",
"sha1",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"subprocess",
"thiserror",
@@ -3591,7 +3590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap 2.0.0",
"indexmap 2.0.1",
]
[[package]]
@@ -3860,7 +3859,7 @@ dependencies = [
"md-5 0.10.6",
"memchr",
"rand 0.8.5",
"sha2 0.10.7",
"sha2 0.10.8",
"stringprep",
]
@@ -4506,7 +4505,7 @@ version = "7.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74"
dependencies = [
"sha2 0.10.7",
"sha2 0.10.8",
"walkdir",
]
@@ -4954,7 +4953,7 @@ version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [
"indexmap 2.0.0",
"indexmap 2.0.1",
"itoa",
"ryu",
"serde",
@@ -5047,7 +5046,7 @@ version = "0.9.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
dependencies = [
"indexmap 2.0.0",
"indexmap 2.0.1",
"itoa",
"ryu",
"serde",
@@ -5097,9 +5096,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.7"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -5108,9 +5107,9 @@ dependencies = [
[[package]]
name = "sharded-slab"
version = "0.1.4"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b"
dependencies = [
"lazy_static",
]
@@ -5320,9 +5319,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e58421b6bc416714d5115a2ca953718f6c621a51b68e4f4922aea5a4391a721"
checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33"
dependencies = [
"sqlx-core",
"sqlx-macros",
@@ -5333,9 +5332,9 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4cef4251aabbae751a3710927945901ee1d97ee96d757f6880ebb9a79bfd53"
checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d"
dependencies = [
"ahash 0.8.3",
"atoi",
@@ -5355,7 +5354,7 @@ dependencies = [
"futures-util",
"hashlink",
"hex",
"indexmap 2.0.0",
"indexmap 2.0.1",
"log",
"memchr",
"once_cell",
@@ -5365,7 +5364,7 @@ dependencies = [
"rustls-pemfile",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"sqlformat",
"thiserror",
@@ -5379,9 +5378,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "208e3165167afd7f3881b16c1ef3f2af69fa75980897aac8874a0696516d12c2"
checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec"
dependencies = [
"proc-macro2",
"quote",
@@ -5392,9 +5391,9 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a4a8336d278c62231d87f24e8a7a74898156e34c1c18942857be2acb29c7dfc"
checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc"
dependencies = [
"dotenvy",
"either",
@@ -5405,7 +5404,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx-core",
"sqlx-mysql",
"sqlx-postgres",
@@ -5418,9 +5417,9 @@ dependencies = [
[[package]]
name = "sqlx-mysql"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482"
checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
dependencies = [
"atoi",
"base64 0.21.4",
@@ -5451,7 +5450,7 @@ dependencies = [
"rsa 0.9.2",
"serde",
"sha1",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"sqlx-core",
"stringprep",
@@ -5463,9 +5462,9 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e"
checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
dependencies = [
"atoi",
"base64 0.21.4",
@@ -5494,7 +5493,7 @@ dependencies = [
"serde",
"serde_json",
"sha1",
"sha2 0.10.7",
"sha2 0.10.8",
"smallvec",
"sqlx-core",
"stringprep",
@@ -5506,9 +5505,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4c21bf34c7cae5b283efb3ac1bcc7670df7561124dc2f8bdc0b59be40f79a2"
checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f"
dependencies = [
"atoi",
"chrono",
@@ -6063,18 +6062,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.48"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.48"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
dependencies = [
"proc-macro2",
"quote",
@@ -6330,7 +6329,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.0.0",
"indexmap 2.0.1",
"serde",
"serde_spanned",
"toml_datetime",
@@ -7098,7 +7097,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"axum",
@@ -7116,7 +7115,7 @@ dependencies = [
"rsmq_async",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx",
"tokio",
"tokio-metrics",
@@ -7132,7 +7131,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"argon2",
@@ -7169,7 +7168,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"sha2 0.10.7",
"sha2 0.10.8",
"sql-builder",
"sqlx",
"tempfile",
@@ -7192,7 +7191,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"base64 0.21.4",
"chrono",
@@ -7210,7 +7209,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"chrono",
"serde",
@@ -7223,7 +7222,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"axum",
@@ -7239,7 +7238,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx",
"thiserror",
"tokio",
@@ -7251,7 +7250,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"serde",
"serde_json",
@@ -7259,7 +7258,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -7270,7 +7269,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"gosyn",
@@ -7282,7 +7281,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -7293,7 +7292,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"itertools 0.11.0",
@@ -7304,7 +7303,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"async-recursion",
@@ -7321,7 +7320,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"lazy_static",
@@ -7332,7 +7331,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -7349,7 +7348,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"getrandom 0.2.10",
@@ -7367,7 +7366,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"async-recursion",
@@ -7397,7 +7396,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.177.0"
version = "1.178.1"
dependencies = [
"anyhow",
"async-recursion",
@@ -7432,7 +7431,7 @@ dependencies = [
"rust_decimal",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.8",
"sqlx",
"tokio",
"tokio-postgres",

View File

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

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,2 @@
-- Add up migration script here
INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) SELECT id, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{"g/all": false}' FROM workspace ON CONFLICT DO NOTHING;

View File

@@ -89,11 +89,9 @@ pub async fn initial_load(
};
let reload_base_url_f = async {
if let Err(e) = reload_base_url_setting(db).await {
tracing::error!("Error reloading base url: {:?}", e)
}
if let Err(e) = reload_base_url_setting(db).await {
tracing::error!("Error reloading base url: {:?}", e)
}
};
let reload_server_config_f = async {
@@ -114,11 +112,9 @@ pub async fn initial_load(
};
let reload_license_key_f = async {
if server_mode {
#[cfg(feature = "enterprise")]
if let Err(e) = reload_license_key(&db).await {
tracing::error!("Error reloading license key: {:?}", e)
}
#[cfg(feature = "enterprise")]
if let Err(e) = reload_license_key(&db).await {
tracing::error!("Error reloading license key: {:?}", e)
}
};
@@ -323,12 +319,16 @@ pub async fn monitor_db<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
};
let verify_license_key_f = async {
if server_mode {
#[cfg(feature = "enterprise")]
if let Err(e) = verify_license_key().await {
tracing::error!("Error verifying license key: {:?}", e);
#[cfg(feature = "enterprise")]
if let Err(e) = verify_license_key().await {
tracing::error!("Error verifying license key: {:?}", e);
let mut l = LICENSE_KEY_VALID.write().await;
*l = false;
} else {
let is_valid = LICENSE_KEY_VALID.read().await.clone();
if !is_valid {
let mut l = LICENSE_KEY_VALID.write().await;
*l = false;
*l = true;
}
}
};

View File

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

View File

@@ -958,6 +958,12 @@ async fn create_workspace(
.execute(&mut *tx)
.await?;
sqlx::query!(
"INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms) VALUES ($1, 'app_groups', 'App Groups', ARRAY[]::TEXT[], '{\"g/all\": false}') ON CONFLICT DO NOTHING",
nw.id,
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"INSERT INTO resource (workspace_id, path, value, description, resource_type) VALUES ($1, 'f/app_themes/theme_0', '{\"name\": \"Default Theme\", \"value\": \"\"}', 'The default app theme', 'app_theme') ON CONFLICT DO NOTHING",

View File

@@ -17,7 +17,11 @@ pub fn username_to_permissioned_as(user: &str) -> String {
}
pub fn truncate_token(token: &str) -> String {
let mut s = token[..10].to_owned();
s.push_str("*****");
s
if token.len() > 10 {
let mut s = token[..10].to_owned();
s.push_str("*****");
s
} else {
token.to_string()
}
}

View File

@@ -162,6 +162,22 @@ pub async fn load_worker_config(db: &DB) -> error::Result<WorkerConfig> {
WorkspacedPath { workspace_id: workspace.to_string(), path: script_path.to_string() }
}
});
if *WORKER_GROUP == "default" {
let mut all_tags = config
.worker_tags
.unwrap_or_default()
.into_iter()
.chain(
std::env::var("WORKER_TAGS")
.ok()
.map(|x| x.split(',').map(|x| x.to_string()).collect_vec())
.unwrap_or_default(),
)
.sorted()
.collect_vec();
all_tags.dedup();
config.worker_tags = Some(all_tags);
}
Ok(WorkerConfig {
worker_tags: config
.worker_tags

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

View File

@@ -31,7 +31,7 @@ addEventListener("error", (event) => {
}
});
export const VERSION = "v1.177.0";
export const VERSION = "v1.178.1";
let command: any = new Command()
.name("wmill")

View File

@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.177.0",
"version": "1.178.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.177.0",
"version": "1.178.1",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",

View File

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

View File

@@ -2,7 +2,6 @@
import { ScheduleService } from '$lib/gen'
import { emptyString, formatCron } from '$lib/utils'
import Badge from './Badge.svelte'
// @ts-ignore
import Multiselect from 'svelte-multiselect'
import TimezonePicker from 'svelte-timezone-picker'
@@ -15,6 +14,11 @@
export let disabled = false
export let validCRON = true
// If the timezone is not a valid timezone, default to Europe/Paris
$: if (!timezone.includes('/')) {
timezone = 'Europe/Paris'
}
let preview: string[] = []
// If the user has already entered a cron string, switching to the basic tab will override it.
let executeEvery: 'second' | 'minute' | 'hour' | 'day-month' | 'month' | 'day-week' = 'minute'

View File

@@ -235,8 +235,14 @@
let command: Disposable | undefined = undefined
let sqlSchemaCompletor: Disposable | undefined = undefined
$: args &&
(dbSchema = $dbSchemas[(lang === 'graphql' ? args.api : args.database)?.replace('$res:', '')])
function updateSchema(lang, args) {
const schemaRes = lang === 'graphql' ? args.api : args.database
if (schemaRes instanceof String) {
dbSchema = $dbSchemas[schemaRes.replace('$res:', '')]
}
}
$: args && updateSchema(lang, args)
$: dbSchema && ['sql', 'graphql'].includes(lang) && addDBSchemaCompletions()
$: (!dbSchema || lang !== 'sql') && sqlSchemaCompletor && sqlSchemaCompletor.dispose()
$: (!dbSchema || lang !== 'graphql') && graphqlService && graphqlService.setSchemaConfig([])

View File

@@ -61,7 +61,7 @@
<input type="text" placeholder="name (optional)" bind:value={name} />
</div>
<div>
<input type="text" placeholder="password" bind:value={company} />
<input type="text" placeholder="company (optional)" bind:value={company} />
</div>
<div class="text-xs text-tertiary grow text-right"> Email will be sent if SMTP configured </div>
</div>

View File

@@ -57,7 +57,7 @@
<div class="text-sm text-secondary border p-2">
Create a new OAuth 2.0 Client <a
href="https://console.cloud.google.com/apis/credentials"
target="_blank">in Google console</a
target="_blank">in google console</a
>
and set the redirect URI to <code>BASE_URL/user/login_callback/google</code>
where BASE_URL is what you configured as core BASE_URL
@@ -72,7 +72,7 @@
<div class="text-sm text-secondary border p-2">
Create a new OAuth 2.0 Client <a
href="https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade"
target="_blank">in Microsoft portal</a
target="_blank">in microsoft portal</a
>
and in the "Authentication" tab, set the redirect URI to
<code>BASE_URL/user/login_callback/microsoft</code>, the logout channel to

View File

@@ -74,7 +74,7 @@
</ConfirmationModal>
<div class="flex gap-2 items-center"
><h4 class="py-4 truncate w-40">{name}</h4>
><h4 class="py-4 truncate w-40 text-primary">{name}</h4>
{#if $superadmin}
<Popup
floatingConfig={{ strategy: 'absolute', placement: top ? 'top-start' : 'bottom-start' }}

View File

@@ -29,7 +29,8 @@
export const staticOutputs: string[] = ['loading', 'result', 'jobId']
const { app, worldStore, stateId } = getContext<AppViewerContext>('AppViewerContext')
const { app, worldStore, stateId, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
const outputs = initOutput($worldStore, id, {
result: undefined,
@@ -42,6 +43,12 @@
configuration
)
$componentControl[id] = {
setValue(nvalue: string) {
wrapper?.setArgs(nvalue)
}
}
let runnableComponent: RunnableComponent
let loading = false
@@ -50,6 +57,8 @@
(componentInput?.type != 'runnable' || Object.keys(componentInput?.fields ?? {}).length == 0)
let css = initCss($app.css?.formcomponent, customCss)
let wrapper: RunnableWrapper
</script>
{#each Object.keys(components['formcomponent'].initialData.configuration) as key (key)}
@@ -74,6 +83,7 @@
<RunnableWrapper
{recomputeIds}
{render}
bind:this={wrapper}
bind:runnableComponent
bind:loading
{componentInput}

View File

@@ -79,6 +79,10 @@
$runnableComponents = $runnableComponents
export function setArgs(value: any) {
args = value
}
let args: Record<string, any> | undefined = undefined
let runnableInputValues: Record<string, any> = {}
let executeTimeout: NodeJS.Timeout | undefined = undefined

View File

@@ -22,6 +22,8 @@
| 'errorOverlay'
| 'openModal'
| 'closeModal'
| 'open'
| 'close'
configuration: {
gotoUrl: { url: string | undefined; newTab: boolean | undefined }
setTab: {
@@ -40,6 +42,12 @@
closeModal?: {
modalId: string | undefined
}
open?: {
id: string | undefined
}
close?: {
id: string | undefined
}
}
}
| undefined
@@ -69,6 +77,10 @@
export let errorHandledByComponent: boolean = false
export let hasChildrens: boolean = false
export function setArgs(value: any) {
runnableComponent?.setArgs(value)
}
const { staticExporter, noBackend, componentControl, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
@@ -168,6 +180,22 @@
$componentControl[modalId].closeModal?.()
break
}
case 'open': {
const id = sideEffect?.configuration?.open?.id
if (!id) return
$componentControl[id].open?.()
break
}
case 'close': {
const id = sideEffect?.configuration?.close?.id
if (!id) return
$componentControl[id].close?.()
break
}
default:
break
}

View File

@@ -20,7 +20,7 @@ export function computeGlobalContext(world: World | undefined, extraContext: any
function create_context_function_template(eval_string: string, context, noReturn: boolean) {
return `
return async function (context, state, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal) {
return async function (context, state, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close) {
"use strict";
${
Object.keys(context).length > 0
@@ -50,7 +50,9 @@ function make_context_evaluator(
setValue,
setSelectedIndex,
openModal,
closeModal
closeModal,
open,
close
) => Promise<any> {
let template = create_context_function_template(eval_string, context, noReturn)
let functor = Function(template)
@@ -104,6 +106,8 @@ export async function eval_like(
setSelectedIndex?: (index: number) => void
openModal?: () => void
closeModal?: () => void
open?: () => void
close?: () => void
}
>,
worldStore: World | undefined,
@@ -160,6 +164,12 @@ export async function eval_like(
},
(id) => {
controlComponents[id]?.closeModal?.()
},
(id) => {
controlComponents[id]?.open?.()
},
(id) => {
controlComponents[id]?.close?.()
}
)
}

View File

@@ -21,8 +21,15 @@
export let noWFull = false
export let render: boolean
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, mode } =
getContext<AppViewerContext>('AppViewerContext')
const {
app,
focusedGrid,
selectedComponent,
worldStore,
connectingInput,
mode,
componentControl
} = getContext<AppViewerContext>('AppViewerContext')
const resolvedConfig = initConfig(
components['drawercomponent'].initialData.configuration,
@@ -32,6 +39,15 @@
let appDrawer: Drawer
$componentControl[id] = {
open: () => {
appDrawer?.openDrawer()
},
close: () => {
appDrawer?.closeDrawer()
}
}
let css = initCss($app.css?.drawercomponent, customCss)
</script>

View File

@@ -69,17 +69,19 @@
initialData: Array<any> | undefined = [],
page: number = 0
) {
const sanitizedData = Array.isArray(initialData) ? initialData : []
if (mode === 'auto') {
const pageSize: number = configuration.auto.pageSize ?? 0
const data = initialData?.slice(0 + page * pageSize, pageSize + page * pageSize) ?? []
const shouldDisplayPagination = pageSize < initialData?.length ?? false
const total = Math.ceil(initialData?.length / pageSize ?? 0)
const data = sanitizedData?.slice(0 + page * pageSize, pageSize + page * pageSize) ?? []
const shouldDisplayPagination = pageSize < sanitizedData?.length ?? false
const total = Math.ceil(sanitizedData.length / pageSize ?? 0)
return {
data,
shouldDisplayPagination,
indexOffset: page * pageSize,
disableNext: pageSize > 0 && (page + 1) * pageSize >= initialData?.length,
disableNext: pageSize > 0 && (page + 1) * pageSize >= sanitizedData?.length,
total: total
}
} else {
@@ -88,7 +90,7 @@
return {
shouldDisplayPagination: true,
data: initialData ?? [],
data: sanitizedData ?? [],
indexOffset: 0,
disableNext: page + 1 >= pageCount,
total: total

View File

@@ -64,6 +64,12 @@
},
closeModal: () => {
open = false
},
open: () => {
open = true
},
close: () => {
open = false
}
}
</script>
@@ -148,7 +154,7 @@
<div>{resolvedConfig.modalTitle}</div>
<div class="w-8">
<button
on:click={() => {
on:click|stopPropagation={() => {
open = false
}}
class="hover:bg-surface-hover bg-surface-secondary rounded-full w-8 h-8 flex items-center justify-center transition-all"

View File

@@ -168,7 +168,8 @@
let selectedTab: 'insert' | 'settings' | 'css' = 'insert'
let befSelected: string | undefined = undefined
$: if ($selectedComponent?.[0] != befSelected) {
$: if ($selectedComponent?.[0] != befSelected && $selectedComponent?.[0] != undefined) {
befSelected = $selectedComponent?.[0]
selectedTab = 'settings'
@@ -452,7 +453,7 @@
)}
style={$appStore.css?.['app']?.['viewer']?.style}
>
<div class="absolute bottom-4 left-4 z-50">
<div class="absolute bottom-2 left-4 z-50">
<div class="flex flex-row gap-2 text-xs items-center">
<Button
color="light"
@@ -465,7 +466,7 @@
>
<Minus size={14} />
</Button>
{$scale}
{$scale}%
<Button
color="light"
variant="border"
@@ -569,7 +570,12 @@
<div slot="content" class="h-full overflow-y-auto">
<TabContent class="overflow-auto h-full" value="settings">
{#if $selectedComponent !== undefined}
<SettingsPanel />
<SettingsPanel
on:delete={() => {
befSelected = undefined
selectedTab = 'insert'
}}
/>
<SecondaryMenu right />
{:else}
<div class="min-w-[150px] text-sm text-secondary text-center py-8 px-2">

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { createEventDispatcher, getContext } from 'svelte'
import type { App, AppViewerContext } from '../types'
import { BG_PREFIX, allItems } from '../utils'
import { findComponentSettings, findGridItem } from './appUtils'
@@ -32,11 +32,18 @@
})
.find((x) => x)
}
const dispatch = createEventDispatcher()
</script>
{#if componentSettings}
{#key componentSettings?.item?.id}
<ComponentPanel bind:componentSettings />
<ComponentPanel
bind:componentSettings
onDelete={() => {
dispatch('delete')
}}
/>
{/key}
{:else if tableActionSettings}
{#key tableActionSettings?.item?.data?.id}

View File

@@ -323,6 +323,55 @@ export function insertNewGridItem(
return id
}
export function copyComponent(
app: App,
item: GridItem,
parentGrid: FocusedGrid | undefined,
subgrids: Record<string, GridItem[]>,
alreadyVisited: string[]
) {
if (alreadyVisited.includes(item.id)) {
return
} else {
alreadyVisited.push(item.id)
}
const newItem = insertNewGridItem(
app,
(id) => {
if (item.data.type === 'tablecomponent') {
return {
...item.data,
id,
actionButtons:
item.data.actionButtons.map((x) => ({
...x,
id: x.id.replace(`${item.id}_`, `${id}_`)
})) ?? []
}
} else {
return { ...item.data, id }
}
},
parentGrid,
Object.fromEntries(gridColumns.map((column) => [column, item[column]]))
)
for (let i = 0; i < (item?.data?.numberOfSubgrids ?? 0); i++) {
subgrids[`${item.id}-${i}`].forEach((subgridItem) => {
copyComponent(
app,
subgridItem,
{ parentComponentId: newItem, subGridIndex: i },
subgrids,
alreadyVisited
)
})
}
return newItem
}
export function getAllSubgridsAndComponentIds(
app: App,
component: AppComponent

View File

@@ -1,12 +1,13 @@
<script lang="ts">
import { getContext } from 'svelte'
import {
copyComponent,
findGridItem,
findGridItemParentGrid,
getAllSubgridsAndComponentIds,
insertNewGridItem
} from '../appUtils'
import type { AppEditorContext, AppViewerContext, FocusedGrid, GridItem } from '../../types'
import type { AppEditorContext, AppViewerContext, GridItem } from '../../types'
import { push } from '$lib/history'
import { sendUserToast } from '$lib/toast'
import { gridColumns } from '../../gridUtils'
@@ -242,7 +243,7 @@
} else if (copiedGridItems) {
let nitems: string[] = []
for (let copiedGridItem of copiedGridItems) {
let newItem = copyComponent(copiedGridItem, $focusedGrid, subgrids, [])
let newItem = copyComponent($app, copiedGridItem, $focusedGrid, subgrids, [])
newItem && nitems.push(newItem)
}
$selectedComponent = nitems.map((x) => x)
@@ -251,52 +252,6 @@
$app = $app
}
function copyComponent(
item: GridItem,
parentGrid: FocusedGrid | undefined,
subgrids: Record<string, GridItem[]>,
alreadyVisited: string[]
) {
if (alreadyVisited.includes(item.id)) {
return
} else {
alreadyVisited.push(item.id)
}
const newItem = insertNewGridItem(
$app,
(id) => {
if (item.data.type === 'tablecomponent') {
return {
...item.data,
id,
actionButtons:
item.data.actionButtons.map((x) => ({
...x,
id: x.id.replace(`${item.id}_`, `${id}_`)
})) ?? []
}
} else {
return { ...item.data, id }
}
},
parentGrid,
Object.fromEntries(gridColumns.map((column) => [column, item[column]]))
)
for (let i = 0; i < (item?.data?.numberOfSubgrids ?? 0); i++) {
subgrids[`${item.id}-${i}`].forEach((subgridItem) => {
copyComponent(
subgridItem,
{ parentComponentId: newItem, subGridIndex: i },
subgrids,
alreadyVisited
)
})
}
return newItem
}
function keydown(event: KeyboardEvent) {
// Ignore keydown events if the user is typing in monaco
let classes = event.target?.['className']

View File

@@ -297,8 +297,8 @@ const labels = {
setTab: 'Set the tab of a tabs component',
sendToast: 'Display a toast notification',
sendErrorToast: 'Display an error toast notification',
openModal: 'Open a modal',
closeModal: 'Close a modal'
open: 'Open a modal or a drawer',
close: 'Close a modal or a drawer'
}
const onSuccessClick = {
@@ -356,6 +356,22 @@ const onSuccessClick = {
type: 'static',
value: ''
}
},
open: {
id: {
tooltip: 'The id of the modal or the drawer to open',
fieldType: 'text',
type: 'static',
value: ''
}
},
close: {
id: {
tooltip: 'The id of the modal or the drawer to close',
fieldType: 'text',
type: 'static',
value: ''
}
}
}
} as const
@@ -406,18 +422,18 @@ const onErrorClick = {
value: true
}
},
openModal: {
modalId: {
tooltip: 'The id of the modal to open',
open: {
id: {
tooltip: 'The id of the modal or the drawer to open',
fieldType: 'text',
type: 'static',
value: '',
noVariablePicker: true
}
},
closeModal: {
modalId: {
tooltip: 'The id of the modal to close',
close: {
id: {
tooltip: 'The id of the modal or the drawer to close',
fieldType: 'text',
type: 'static',
value: '',
@@ -457,8 +473,7 @@ const paginationOneOf = {
}
} as const
const documentationBaseUrl =
'https://www.windmill.dev/docs/apps/app_configuration_settings'
const documentationBaseUrl = 'https://www.windmill.dev/docs/apps/app_configuration_settings'
export const components = {
displaycomponent: {

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import type { AppEditorContext, AppViewerContext } from '../../types'
import { getContext } from 'svelte'
import { getContext, onMount } from 'svelte'
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
import {
components as componentsRecord,
@@ -10,14 +10,28 @@
type TypedComponent
} from '../component'
import ListItem from './ListItem.svelte'
import { appComponentFromType, insertNewGridItem } from '../appUtils'
import { appComponentFromType, copyComponent, insertNewGridItem } from '../appUtils'
import { push } from '$lib/history'
import { ClearableInput } from '../../../common'
import { workspaceStore } from '$lib/stores'
import { getGroup, listGroups } from './groupUtils'
import { LayoutDashboard } from 'lucide-svelte'
const { app, selectedComponent, focusedGrid } = getContext<AppViewerContext>('AppViewerContext')
const { history } = getContext<AppEditorContext>('AppEditorContext')
let groups: Array<{
name: string
path: string
}> = []
async function fetchGroups() {
if ($workspaceStore) {
groups = await listGroups($workspaceStore)
}
}
function addComponent(appComponentType: TypedComponent['type']): void {
push(history, $app)
@@ -33,6 +47,25 @@
$app = $app
}
async function addGroup(group: { name: string; path: string }) {
if (!$workspaceStore) return
const res = await getGroup($workspaceStore, group.path)
console.log(res)
if (!res) return
push(history, $app)
$dirtyStore = true
const id = copyComponent($app, res.value.item, $focusedGrid, res.value.subgrids, [])
if (id) {
$selectedComponent = [id]
$app = $app
}
}
function addPresetComponent(appComponentType: string): void {
const preset = presetsRecord[appComponentType]
@@ -65,6 +98,10 @@
return presetName.includes(search.toLowerCase())
})
}))
onMount(() => {
fetchGroups()
})
</script>
<section class="p-2 sticky w-full z-10 top-0 bg-surface border-b">
@@ -123,6 +160,29 @@
</div>
{/if}
{/each}
<ListItem title={'Groups'}>
<div class="flex flex-wrap gap-3 py-2">
{#if groups}
{#each groups as group (group)}
<div class="w-20">
<button
on:click={() => {
addGroup(group)
}}
title={group.name}
class="transition-all border w-20 shadow-sm h-16 p-2 flex flex-col gap-2 items-center
justify-center bg-surface rounded-md hover:bg-blue-50 dark:hover:bg-blue-900 duration-200 hover:border-blue-500"
>
<LayoutDashboard class="text-secondary" />
</button>
<div class="text-xs text-center flex-wrap text-secondary mt-1">
{group.name}
</div>
</div>
{/each}
{/if}
</div>
</ListItem>
</div>
{/if}
</div>

View File

@@ -76,7 +76,7 @@
class="h-full"
lang="css"
bind:code={$app.theme.css}
fixedOverflowWidgets={false}
fixedOverflowWidgets={true}
small
automaticLayout
bind:this={cssEditor}

View File

@@ -0,0 +1,100 @@
<script lang="ts">
import { listGroups, type Group, createGroup } from './groupUtils'
import { workspaceStore } from '$lib/stores'
import { onMount } from 'svelte'
import Button from '$lib/components/common/button/Button.svelte'
import DataTable from '$lib/components/table/DataTable.svelte'
import Head from '$lib/components/table/Head.svelte'
import Cell from '$lib/components/table/Cell.svelte'
import { sendUserToast } from '$lib/toast'
import { ResourceService } from '$lib/gen'
import GroupRow from './GroupRow.svelte'
import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte'
export let selectedGroup: object = {}
let groups: Array<{
name: string
path: string
}> = []
let loading: boolean = false
async function getGroups() {
loading = true
groups = await listGroups($workspaceStore!)
loading = false
}
async function addGroup(nameField: string) {
const groups = await ResourceService.listResourceNames({
workspace: $workspaceStore!,
name: 'app_group'
})
const group: Group = {
path: 'f/app_groups/group_' + groups.length,
value: {
value: selectedGroup,
name: nameField
}
}
const message = await createGroup($workspaceStore!, group)
getGroups()
nameField = ''
sendUserToast('Group created:' + message)
}
let nameField: string = ''
onMount(() => {
getGroups()
})
</script>
<div class="p-2 flex flex-col items-start w-auto gap-2 relative">
<div class="w-full flex flex-row gap-2 items-center">
<input bind:value={nameField} placeholder={'Group name'} />
<Button on:click={() => addGroup(nameField)} color="dark" size="xs">Create group</Button>
</div>
{#if loading}
<div class="flex flex-col w-full pt-12">
{#each new Array(6) as _}
<Skeleton layout={[[2], 0.5]} />
{/each}
</div>
{:else if Array.isArray(groups) && groups.length > 0}
<div class="w-full">
<DataTable size="xs">
<Head>
<tr>
<Cell first head>Path</Cell>
<Cell last head />
</tr>
</Head>
<tbody class="divide-y">
{#if groups && groups.length > 0}
{#each groups as row}
{#key row}
<GroupRow
{row}
on:reloadGroups={() => {
getGroups()
}}
/>
{/key}
{/each}
{:else}
<tr>Loading...</tr>
{/if}
</tbody>
</DataTable>
</div>
{/if}
</div>

View File

@@ -0,0 +1,39 @@
<script lang="ts">
import { Drawer, DrawerContent } from '$lib/components/common'
import GroupList from './GroupList.svelte'
import { getContext } from 'svelte'
import type { AppViewerContext, GridItem } from '../../types'
import { getAllSubgridsAndComponentIds } from '../appUtils'
export let item: GridItem
const { app } = getContext<AppViewerContext>('AppViewerContext')
$: subgrids = getSubgrids(item)
function getSubgrids(item: GridItem) {
let allSubgrids = {}
let subgrids = getAllSubgridsAndComponentIds($app, item.data)[0]
for (let key of subgrids) {
allSubgrids[key] = $app.subgrids?.[key]
}
return allSubgrids
}
let codeDrawer: Drawer
export async function openDrawer() {
codeDrawer.openDrawer()
}
</script>
<Drawer bind:this={codeDrawer}>
<DrawerContent title="Group manager" on:close={codeDrawer.closeDrawer}>
<GroupList
selectedGroup={{
item,
subgrids
}}
/>
</DrawerContent>
</Drawer>

View File

@@ -0,0 +1,58 @@
<script lang="ts">
import { getGroup, updateGroup } from './groupUtils'
import { workspaceStore } from '$lib/stores'
import Button from '$lib/components/common/button/Button.svelte'
import { Popup } from '$lib/components/common'
import { Pen } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import { sendUserToast } from '$lib/toast'
export let row: {
name: string
path: string
}
let editedName = row.name
const dispatch = createEventDispatcher()
</script>
<Popup
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
let:close
>
<svelte:fragment slot="button">
<Button color="light" size="xs2" nonCaptureEvent={true}>
<div class="flex flex-row gap-1 items-center">
<Pen size={16} />
</div>
</Button>
</svelte:fragment>
<div class="flex flex-col w-80 gap-2">
<div class="leading-6 font-semibold text-xs">Edit group name</div>
<div class="flex flex-row gap-2">
<input bind:value={editedName} />
<Button
color="dark"
size="xs"
on:click={async () => {
if (!$workspaceStore) return
const group = await getGroup($workspaceStore, row.path)
await updateGroup($workspaceStore, row.path, {
value: {
...group,
name: editedName
}
})
dispatch('reloadGroups')
close(null)
sendUserToast('Group name updated:\n' + editedName)
}}
>
Update
</Button>
</div>
</div>
</Popup>

View File

@@ -0,0 +1,60 @@
<script lang="ts">
import { deleteGroup } from './groupUtils'
import { workspaceStore } from '$lib/stores'
import Cell from '$lib/components/table/Cell.svelte'
import { Trash } from 'lucide-svelte'
import { sendUserToast } from '$lib/toast'
import { twMerge } from 'tailwind-merge'
import { createEventDispatcher } from 'svelte'
import GroupNameEditor from './GroupNameEditor.svelte'
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
import { classNames } from '$lib/utils'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
export let row: {
name: string
path: string
}
const dispatch = createEventDispatcher()
async function toggleDelete() {
if ($workspaceStore) {
await deleteGroup($workspaceStore, row.path)
}
dispatch('reloadGroups')
sendUserToast('Group deleted:\n' + row.name)
}
</script>
<tr>
<Cell first>
<div class="flex flex-row gap-1 items-center">
<GroupNameEditor on:reloadGroups {row} />
{row.name}
</div>
</Cell>
<Cell last>
<div class={twMerge('flex flex-row gap-1 justify-end ')}>
<button on:pointerdown|stopPropagation>
<ButtonDropdown hasPadding={false}>
<svelte:fragment slot="items">
<MenuItem on:click={toggleDelete}>
<div
class={classNames(
'!text-red-600 flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
)}
>
<Trash size={16} />
Delete
</div>
</MenuItem>
</svelte:fragment>
</ButtonDropdown>
</button>
</div>
</Cell>
</tr>

View File

@@ -14,20 +14,6 @@ const setTab = {
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#settab'
}
const openModal = {
title: 'openModal',
description: 'Use the openModal function to open a modal.',
example: 'openModal(id: string)',
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#openmodal'
}
const closeModal = {
title: 'closeModal',
description: 'Use the closeModal function to close a modal.',
example: 'closeModal(id: string)',
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#closemodal'
}
const recompute = {
title: 'recompute',
description: 'Use the recompute function to recompute the value of a component.',
@@ -57,6 +43,20 @@ const setSelectedIndex = {
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#setselectedindex'
}
const open = {
title: 'open',
description: 'Use the open function to open a modal or a drawer.',
example: 'open(id: string)',
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#open'
}
const close = {
title: 'close',
description: 'Use the close function to close a modal or a drawer.',
example: 'close(id: string)',
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#close'
}
export function getComponentControl(type: keyof typeof components): Array<ComponentFunction> {
switch (type) {
case 'tabscomponent':
@@ -70,7 +70,9 @@ export function getComponentControl(type: keyof typeof components): Array<Compon
case 'steppercomponent':
return [setTab]
case 'modalcomponent':
return [openModal, closeModal]
return [open, close]
case 'drawercomponent':
return [open, close]
case 'aggridcomponent':
return [getAgGrid, setSelectedIndex]
case 'displaycomponent':

View File

@@ -0,0 +1,73 @@
import { ResourceService } from '$lib/gen'
import { sendUserToast } from '$lib/toast'
export interface Group {
path: string
value: {
value?: object
name: string
}
}
export function createGroup(workspace: string, group: Group): Promise<string> {
const createGroupRequest = {
workspace,
requestBody: {
...group,
resource_type: 'app_group',
value: group.value || ''
}
}
return ResourceService.createResource(createGroupRequest)
}
export async function getGroup(
workspace: string,
path: string
): Promise<{
name: string,
value: any
}> {
try {
return ResourceService.getResourceValue({
workspace,
path
})
} catch (e) {
sendUserToast(`Group not found ${path}`)
return {
value: '',
name: 'Not found'
}
}
}
export function updateGroup(workspace: string, path: string, updatedGroup: any): Promise<string> {
const updateGroupRequest = {
workspace,
path,
requestBody: updatedGroup
}
return ResourceService.updateResource(updateGroupRequest)
}
export function deleteGroup(workspace: string, path: string): Promise<string> {
const deleteGroupRequest = {
workspace,
path: path
}
return ResourceService.deleteResource(deleteGroupRequest)
}
export async function listGroups(workspace: string): Promise<
Array<{
name: string
path: string
}>
> {
const listGroupsRequest = {
workspace,
name: 'app_group'
}
return ResourceService.listResourceNames(listGroupsRequest)
}

View File

@@ -72,7 +72,10 @@
const theme = $app.theme
if (theme?.type === 'path') {
sendUserToast('Cannot migrate to CSS editor when using a theme by path', true)
sendUserToast(
'Cannot migrate to CSS editor when using a theme by path. Fork a global theme to edit CSS.',
true
)
return
} else if (theme?.type === 'inlined') {
let cssString = theme.css

View File

@@ -299,9 +299,8 @@
{/if}
{/key}
</PanelSection>
{/if}
<ComponentControl type={component.type} />
{#if componentSettings.item.data.type === 'tabscomponent'}
@@ -320,7 +319,7 @@
{:else if componentSettings.item.data.type === 'containercomponent'}
<GridGroup
bind:groupFields={componentSettings.item.data.groupFields}
bind:component={componentSettings.item.data}
item={componentSettings.item}
/>
{:else if componentSettings.item.data.type === 'conditionalwrapper'}
<GridCondition
@@ -434,6 +433,5 @@
</div>
</PanelSection>
{/if}
</div>
{/if}

View File

@@ -1,15 +1,16 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
import type { RichConfigurations } from '../../types'
import type { AppComponent } from '../component'
import type { GridItem, RichConfigurations } from '../../types'
import PanelSection from './common/PanelSection.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import InputsSpecsEditor from './InputsSpecsEditor.svelte'
import GroupManagementDrawer from '../componentsPanel/GroupManagementDrawer.svelte'
export let groupFields: RichConfigurations | undefined
export let item: GridItem
export let component: AppComponent
let groupManagementDrawer: GroupManagementDrawer | undefined = undefined
// const { app, runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
@@ -48,6 +49,17 @@
/>
</div>
{#if groupFields != undefined}
<div class="p-2">
<Button
on:click={() => {
groupManagementDrawer?.openDrawer()
}}
size="xs"
color="light"
>
Save group to workspace
</Button>
</div>
<PanelSection
title={`Group Fields ${
Object.keys(groupFields ?? {}).length > 0
@@ -67,7 +79,7 @@
delete groupFields[e.detail]
groupFields = groupFields
}}
id={component.id}
id={item.id}
shouldCapitalize={false}
displayType
deletable
@@ -101,3 +113,5 @@
</div>
</PanelSection>
{/if}
<GroupManagementDrawer bind:this={groupManagementDrawer} {item} />

View File

@@ -234,6 +234,8 @@ export type AppViewerContext = {
setSelectedIndex?: (index: number) => void
openModal?: () => void
closeModal?: () => void
open?: () => void
close?: () => void
}
>
>
@@ -249,7 +251,7 @@ export type AppEditorContext = {
pickVariableCallback: Writable<((path: string) => void) | undefined>
selectedComponentInEditor: Writable<string | undefined>
movingcomponents: Writable<string[] | undefined>
jobsDrawerOpen: Writable<boolean>,
jobsDrawerOpen: Writable<boolean>
scale: Writable<number>
}

View File

@@ -172,20 +172,65 @@ export function buildExtraLib(
return `${cs}
${
goto
? `declare async function goto(path: string, newTab?: boolean): Promise<void>;
? `
/** open a window or tab
* @param path path/url to go to
* @param open in a tab?
*/
declare async function goto(path: string, newTab?: boolean): Promise<void>;
/** set tab
* @param id component's id
* @param index index of the tab to set
*/
declare function setTab(id: string, index: string): void;
/** recompute a component's runnable or background runnable
* @param id component's id
*/
declare function recompute(id: string): void;
/** get the ag grid api from an AgGridTable
* @param id component's id
*/
declare function getAgGrid(id: string): {api: any, columnApi: any} | undefined;
/** set value of a component
* @param id component's id
* @param value value to set
*/
declare function setValue(id: string, value: any): void;
/** set selected index of a table
* @param id component's id
* @param index index to set
*/
declare function setSelectedIndex(id: string, index: number): void;
declare function openModal(id: string): void;
declare function closeModal(id: string): void;
/** close a drawer or modal
* @param id component's id
*/
declare function open(id: string): void;
/** close a drawer or modal
* @param id component's id
*/
declare function close(id: string): void;
`
: ''
}
/** The current's app state */
declare const state: ${JSON.stringify(state)};
/** The iterator within the context of a list */
declare const iter: {index: number, value: any};
/** The row within the context of a table */
declare const row: Record<string, any>;
/** The group fields within the context of a container's group */
declare const group: Record<string, any>;
`

View File

@@ -104,7 +104,13 @@
$: !$generatedCode && hideDiff()
$: dbSchema = $dbSchemas[(lang === 'graphql' ? args.api : args.database)?.replace('$res:', '')]
function updateSchema(lang, args) {
const schemaRes = lang === 'graphql' ? args.api : args.database
if (schemaRes instanceof String) {
dbSchema = $dbSchemas[schemaRes.replace('$res:', '')]
}
}
$: updateSchema(lang, args)
</script>
{#if SUPPORTED_LANGUAGES.has(lang)}

View File

@@ -146,7 +146,13 @@
$: editor && setSelectionHandler()
$: selection && (isEdit = !selection.isEmpty())
$: dbSchema = $dbSchemas[(lang === 'graphql' ? args.api : args.database)?.replace('$res:', '')]
function updateSchema(lang, args) {
const schemaRes = lang === 'graphql' ? args.api : args.database
if (schemaRes instanceof String) {
dbSchema = $dbSchemas[schemaRes.replace('$res:', '')]
}
}
$: updateSchema(lang, args)
</script>
{#if $generatedCode.length > 0 && !genLoading}

View File

@@ -162,6 +162,18 @@ export function createAppFromScript(path: string, schema: Record<string, any> |
type: 'static',
value: ''
}
},
open: {
id: {
type: 'static',
value: ''
}
},
close: {
id: {
type: 'static',
value: ''
}
}
}
},
@@ -207,6 +219,18 @@ export function createAppFromScript(path: string, schema: Record<string, any> |
type: 'static',
value: ''
}
},
open: {
id: {
type: 'static',
value: ''
}
},
close: {
id: {
type: 'static',
value: ''
}
}
}
}
@@ -547,6 +571,18 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
type: 'static',
value: ''
}
},
open: {
id: {
type: 'static',
value: ''
}
},
close: {
id: {
type: 'static',
value: ''
}
}
}
},
@@ -592,6 +628,18 @@ export function createAppFromFlow(path: string, schema: Record<string, any> | un
type: 'static',
value: ''
}
},
open: {
id: {
type: 'static',
value: ''
}
},
close: {
id: {
type: 'static',
value: ''
}
}
}
}

View File

@@ -7,15 +7,20 @@
export let last: boolean = false
export let numeric: boolean = false
export let head: boolean = false
export let shouldStopPropagation: boolean = false
let Tag = head ? 'th' : 'td'
const { size } = getContext<DatatableContext>('datatable')
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<svelte:element
this={Tag}
{...$$restProps}
on:click={(e) => {
if (shouldStopPropagation) e.stopPropagation()
}}
class={twMerge(
'px-2 py-3.5 text-left text-xs text-primary font-normal whitespace-nowrap',
first ? 'sm:pl-6' : '',

View File

@@ -141,7 +141,7 @@
<FolderUsageInfo {name} tabular />
<Cell><FolderInfo members={computeMembers(owners, extra_perms)} /></Cell>
<Cell>
<Cell shouldStopPropagation>
<Dropdown
placement="bottom-end"
dropdownItems={[

View File

@@ -128,25 +128,28 @@
</script>
<CenteredModal title="New Workspace">
<label class="block pb-2 pt-4">
<label class="block pb-4 pt-4">
<span class="text-secondary text-sm">Workspace name</span>
<input type="text" bind:value={name} />
<span class="ml-4 text-tertiary text-xs">Displayable name</span>
<input autofocus type="text" bind:value={name} />
</label>
<label class="block pb-2">
<label class="block pb-4">
<span class="text-secondary text-sm">Workspace ID</span>
<span class="ml-10 text-tertiary text-xs">Slug to uniquely identify your workspace</span>
{#if errorId}
<span class="text-red-500 text-xs">{errorId}</span>
{/if}
<input type="text" bind:value={id} class:input-error={errorId != ''} />
</label>
<label class="block pb-2">
<label class="block pb-4">
<span class="text-secondary text-sm">Your username in that workspace</span>
<input type="text" bind:value={username} on:keyup={handleKeyUp} />
{#if errorUser}
<span class="text-red-500 text-xs">{errorUser}</span>
{/if}
</label>
<label class="block pb-2">
<label class="block pb-4">
<span class="text-secondary text-sm"
>OpenAI key for codegen<span class="text-2xs text-tertiary ml-2"
>(optional but recommended)</span

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.177.0"
version = "1.178.1"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
@@ -16,7 +16,7 @@ include = ["wmill/py.typed"]
[tool.poetry.dependencies]
python = "^3.7"
windmill-api = "^1.177.0"
windmill-api = "^1.178.1"
[build-system]
requires = ["poetry>=1.0.2", "poetry-dynamic-versioning"]

View File

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

View File

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

View File

@@ -1 +1 @@
1.177.0
1.178.1