diff --git a/.github/workflows/build_windows_worker_.yml b/.github/workflows/build_windows_worker_.yml new file mode 100644 index 0000000000..256ce0de1a --- /dev/null +++ b/.github/workflows/build_windows_worker_.yml @@ -0,0 +1,58 @@ +name: Build windows executable for this branch + +on: + workflow_dispatch: + +env: + CARGO_INCREMENTAL: 0 + SQLX_OFFLINE: true + DISABLE_EMBEDDING: true + RUST_LOG: info + +jobs: + cargo_build_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Read EE repo commit hash + shell: pwsh + run: | + $ee_repo_ref = Get-Content .\backend\ee-repo-ref.txt + echo "ee_repo_ref=$ee_repo_ref" | Out-File -FilePath $env:GITHUB_ENV -Append + + - name: Checkout windmill-ee-private repository + uses: actions/checkout@v4 + with: + repository: windmill-labs/windmill-ee-private + path: ./windmill-ee-private + ref: ${{ env.ee_repo_ref }} + token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} + fetch-depth: 0 + + - name: Substitute EE code + shell: bash + run: | + ./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private + + - name: Cargo build windows + timeout-minutes: 90 + run: | + vcpkg.exe install openssl-windows:x64-windows + vcpkg.exe install openssl:x64-windows-static + vcpkg.exe integrate install + $env:VCPKGRS_DYNAMIC=1 + $env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static" + mkdir frontend/build && cd backend + New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core + + - name: Rename binary with corresponding architecture + run: | + Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe" + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: windmill-ee-binary + path: ./backend/target/release/windmill-ee.exe diff --git a/.github/workflows/build_windows_worker.yml b/.github/workflows/publish_windows_worker.yml similarity index 100% rename from .github/workflows/build_windows_worker.yml rename to .github/workflows/publish_windows_worker.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 7faf7d54b4..9dfc4d73b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # Changelog +## [1.423.2](https://github.com/windmill-labs/windmill/compare/v1.423.1...v1.423.2) (2024-11-13) + + +### Bug Fixes + +* fix intempestive expr type change in flow transform ([84bd06e](https://github.com/windmill-labs/windmill/commit/84bd06e90650e949c11e2b09447a7bad1ab60a95)) + +## [1.423.1](https://github.com/windmill-labs/windmill/compare/v1.423.0...v1.423.1) (2024-11-12) + + +### Bug Fixes + +* **autoscaling:** autoscaling thresholds to be >= and not > ([de00944](https://github.com/windmill-labs/windmill/commit/de00944f09699ff33a382f5a14f515ccf90b2454)) + +## [1.423.0](https://github.com/windmill-labs/windmill/compare/v1.422.1...v1.423.0) (2024-11-12) + + +### Features + +* s3 input available for public apps ([#4685](https://github.com/windmill-labs/windmill/issues/4685)) ([1671005](https://github.com/windmill-labs/windmill/commit/167100510032ab53cd609fb2c7629e67faceb093)) + + +### Bug Fixes + +* prevent underflow for autoscaling scalein ([5e9870a](https://github.com/windmill-labs/windmill/commit/5e9870a8a993032ec7d45c62e0023008da42c74a)) +* support multiple pip-extra-index-url with commas ([50861fc](https://github.com/windmill-labs/windmill/commit/50861fccc0cd86d4c76120c3306adf94f375330e)) + +## [1.422.1](https://github.com/windmill-labs/windmill/compare/v1.422.0...v1.422.1) (2024-11-12) + + +### Bug Fixes + +* fix password inputs ([e5e174a](https://github.com/windmill-labs/windmill/commit/e5e174ae9516f4c6b94ceb6e258b467f5c9a1f1a)) + +## [1.422.0](https://github.com/windmill-labs/windmill/compare/v1.421.2...v1.422.0) (2024-11-11) + + +### Features + +* expandable subflows in flows ([#4683](https://github.com/windmill-labs/windmill/issues/4683)) ([d44976f](https://github.com/windmill-labs/windmill/commit/d44976f35e45ade510d1ec220b5a1503e11f3db9)) +* **frontend:** critical alerts UI ([#4653](https://github.com/windmill-labs/windmill/issues/4653)) ([d9148ea](https://github.com/windmill-labs/windmill/commit/d9148eaa78680a93d81d71847a7df67e01f3c110)) + ## [1.421.2](https://github.com/windmill-labs/windmill/compare/v1.421.1...v1.421.2) (2024-11-08) diff --git a/backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json b/backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json deleted file mode 100644 index e4909fc4da..0000000000 --- a/backend/.sqlx/query-00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO alerts (alert_type, message) VALUES ('recovered_critical_error', $1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "00ce4ed3ca0eac7cb6283b047353a64b9e78c4beb423f04baef9a53fbf87e9f9" -} diff --git a/backend/.sqlx/query-0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995.json b/backend/.sqlx/query-0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995.json new file mode 100644 index 0000000000..820e4f45ab --- /dev/null +++ b/backend/.sqlx/query-0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995.json @@ -0,0 +1,48 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, alert_type, message, created_at, acknowledged \n FROM alerts \n WHERE acknowledged = $1\n ORDER BY created_at DESC \n LIMIT $2 OFFSET $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "alert_type", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "acknowledged", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Bool", + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true + ] + }, + "hash": "0b955f2cff82a2d4ba3840588143e08952f029480d4a42503ecc3c5e70437995" +} diff --git a/backend/.sqlx/query-31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9.json b/backend/.sqlx/query-31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9.json new file mode 100644 index 0000000000..1295a4b36f --- /dev/null +++ b/backend/.sqlx/query-31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM completed_job\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "31dd23f6768052e486668172e15eba1a3f9b6a8e5678a7ee8e5456ff4405d6f9" +} diff --git a/backend/.sqlx/query-3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e.json b/backend/.sqlx/query-3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e.json new file mode 100644 index 0000000000..5a79a3d793 --- /dev/null +++ b/backend/.sqlx/query-3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('recovered_critical_error', $1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "3a94ad52c6b7cde844fa868167248cd9ff63e5fdfa1d93d8fbec32a257b6b05e" +} diff --git a/backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json b/backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json deleted file mode 100644 index 32295ea1e1..0000000000 --- a/backend/.sqlx/query-4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO alerts (alert_type, message) VALUES ('critical_error', $1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "4c0067c2135a259aea5cc2db60f7375a9a33671be8ef406427d90f67a98c9c9f" -} diff --git a/backend/.sqlx/query-4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91.json b/backend/.sqlx/query-4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91.json new file mode 100644 index 0000000000..b57d834f87 --- /dev/null +++ b/backend/.sqlx/query-4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('critical_error', $1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "4d22084a5d9860832f30e8f08cbfa1848ed3c1336fa4790f45b8189c4ac97d91" +} diff --git a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json index 1fa370e682..75b8108281 100644 --- a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json +++ b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "bool", + "name": "?column?", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18.json b/backend/.sqlx/query-a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284.json similarity index 82% rename from backend/.sqlx/query-0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18.json rename to backend/.sqlx/query-a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284.json index a4c8e3ea0c..13f1319f04 100644 --- a/backend/.sqlx/query-0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18.json +++ b/backend/.sqlx/query-a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $11)\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14 FROM uuid_table) \n RETURNING id", + "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $11)\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, raw_code, raw_lock, raw_flow, flow_status)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15, $16, $17, $18 FROM uuid_table) \n RETURNING id", "describe": { "columns": [ { @@ -72,12 +72,16 @@ "Int4", "Int4", "Int4", - "Int4" + "Int4", + "Text", + "Text", + "Jsonb", + "Jsonb" ] }, "nullable": [ false ] }, - "hash": "0a686ca61444d7ad7484071727aa039a6ea6697e5a49a633b767c052aa3e0a18" + "hash": "a69ba7471d1a5faa145bebbd17d43e6dbe02e9e92ebbc31a50c99c3a04719284" } diff --git a/backend/.sqlx/query-a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d.json b/backend/.sqlx/query-a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d.json new file mode 100644 index 0000000000..53ef15ffa5 --- /dev/null +++ b/backend/.sqlx/query-a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts SET acknowledged = true WHERE acknowledged = false", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "a7c5008aa7ea43d0afac7d9f19846976ed7af2e90270902f001115e023cb947d" +} diff --git a/backend/.sqlx/query-aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42.json b/backend/.sqlx/query-aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42.json new file mode 100644 index 0000000000..0eee98b120 --- /dev/null +++ b/backend/.sqlx/query-aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM queue\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "aa6907b7266ee437dfbd77a9bf6c047fda88f3e72a0d10323a5e4020cf857c42" +} diff --git a/backend/.sqlx/query-ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a.json b/backend/.sqlx/query-ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a.json new file mode 100644 index 0000000000..c97780ba06 --- /dev/null +++ b/backend/.sqlx/query-ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT flow_version.value AS \"value: sqlx::types::Json>\" FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 AND flow.workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "value: sqlx::types::Json>", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "ae543dfa106fa6ad4e9bf45cda1110d4702a80f455c63af6fcbd7cf45bbc4f7a" +} diff --git a/backend/.sqlx/query-be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580.json b/backend/.sqlx/query-be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580.json new file mode 100644 index 0000000000..e726f11fe2 --- /dev/null +++ b/backend/.sqlx/query-be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts SET acknowledged = true WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [] + }, + "hash": "be3ae231557e794172336bc27d725f862dcf039bbb3d75ced9d54c86f53d2580" +} diff --git a/backend/.sqlx/query-cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b.json b/backend/.sqlx/query-cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b.json new file mode 100644 index 0000000000..18bc159e6c --- /dev/null +++ b/backend/.sqlx/query-cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, alert_type, message, created_at, acknowledged \n FROM alerts \n ORDER BY created_at DESC \n LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "alert_type", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "acknowledged", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true + ] + }, + "hash": "cc5ab80241b88c5befea279f16c4ec68cec17b31dcd277b321f652917346496b" +} diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 7fe1f09907..d37f3cb23a 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -576,7 +576,7 @@ dependencies = [ "serde-aux", "serde_json", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -659,7 +659,7 @@ dependencies = [ "sha2 0.10.8", "smart-default", "smol_str", - "thiserror", + "thiserror 1.0.69", "tokio", "uuid 0.8.2", ] @@ -692,7 +692,7 @@ dependencies = [ "crc32fast", "log", "pin-project", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -1363,9 +1363,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5327f6c99920069d1fe374aa743be1af0031dea9f250852cdf1ae6a0861ee24" +checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" dependencies = [ "borsh-derive", "cfg_aliases", @@ -1373,9 +1373,9 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10aedd8f1a81a8aafbfde924b0e3061cd6fedd6f6bbcfc6a76e6fd426d7bfe26" +checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" dependencies = [ "once_cell", "proc-macro-crate", @@ -1545,7 +1545,7 @@ dependencies = [ "rand_distr", "rayon", "safetensors", - "thiserror", + "thiserror 1.0.69", "yoke", "zip", ] @@ -1562,7 +1562,7 @@ dependencies = [ "rayon", "safetensors", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1598,9 +1598,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.37" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "jobserver", "libc", @@ -1714,9 +1714,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -1724,9 +1724,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -1748,9 +1748,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "cmake" @@ -1783,13 +1783,13 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "e0d05af1e006a2407bedef5af410552494ce5be9090444dbbcb57258c1af3d56" dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -1816,7 +1816,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -1943,9 +1943,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -2080,9 +2080,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -2553,7 +2553,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "sourcemap 9.0.1", + "sourcemap 9.1.0", "swc_atoms", "swc_common", "swc_config", @@ -2576,8 +2576,8 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", - "thiserror", - "unicode-width", + "thiserror 1.0.69", + "unicode-width 0.1.14", "url", ] @@ -2715,7 +2715,7 @@ dependencies = [ "strum 0.25.0", "strum_macros 0.25.3", "syn 2.0.87", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2725,7 +2725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4889646c1ce8437a6fde3acb057fd7e2d039e62c61f5063fc125ed1ede114dc6" dependencies = [ "percent-encoding", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -3654,7 +3654,7 @@ dependencies = [ "rustls-pemfile 1.0.4", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "tracing", @@ -3808,7 +3808,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -3900,7 +3900,7 @@ checksum = "5eb37859fda6792e95231aef1c5838f4043ec0ee352d8313421e311c606df612" dependencies = [ "anyhow", "strum 0.25.0", - "thiserror", + "thiserror 1.0.69", "unic-ucd-category", ] @@ -4067,7 +4067,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "ureq", ] @@ -4560,15 +4560,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -5319,7 +5319,7 @@ dependencies = [ "quote", "syn 2.0.87", "termcolor", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -5345,7 +5345,7 @@ dependencies = [ "serde", "serde_json", "socket2 0.5.7", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-native-tls", "tokio-util", @@ -5386,7 +5386,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "subprocess", - "thiserror", + "thiserror 1.0.69", "time", "uuid 1.11.0", "zstd 0.13.2", @@ -5578,7 +5578,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -5700,7 +5700,7 @@ dependencies = [ "serde_with", "sha2 0.10.8", "subtle", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -6352,7 +6352,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.87", - "thiserror", + "thiserror 1.0.69", "typify", "unicode-ident", ] @@ -6391,7 +6391,7 @@ dependencies = [ "lazy_static", "memchr", "parking_lot", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -6428,9 +6428,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -6519,9 +6519,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -6530,26 +6530,29 @@ dependencies = [ "rustc-hash 2.0.0", "rustls 0.23.16", "socket2 0.5.7", - "thiserror", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom 0.2.15", "rand 0.8.5", "ring 0.17.8", "rustc-hash 2.0.0", "rustls 0.23.16", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] @@ -6762,7 +6765,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -6773,7 +6776,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -6788,9 +6791,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -7075,7 +7078,7 @@ dependencies = [ "radix_fmt", "rand 0.8.5", "redis", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7184,16 +7187,16 @@ checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440" dependencies = [ "serde", "tempfile", - "thiserror", + "thiserror 1.0.69", "toml 0.8.19", "toolchain_find", ] [[package]] name = "rustix" -version = "0.38.39" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -7290,6 +7293,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-tokio-stream" @@ -7427,7 +7433,7 @@ dependencies = [ "quick-xml 0.30.0", "rand 0.8.5", "serde", - "thiserror", + "thiserror 1.0.69", "url", "uuid 1.11.0", ] @@ -7539,9 +7545,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -7579,9 +7585,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -7620,9 +7626,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -7680,7 +7686,7 @@ checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7691,7 +7697,7 @@ checksum = "8cac3f1e2ca2fe333923a1ae72caca910b98ed0630bb35ef6f8c8517d6e81afa" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7736,7 +7742,7 @@ dependencies = [ "num-bigint", "serde", "smallvec", - "thiserror", + "thiserror 1.0.69", "v8", ] @@ -7900,7 +7906,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -8043,9 +8049,9 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.0.1" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86721155737d89818020dc35713595c6e7b21016073c101e6590c47ca58c2d16" +checksum = "4d146f02f4bbbabbbe3da0f9cd3ea2ab779defc4ed1f070b5bd83ea48ed78811" dependencies = [ "base64-simd 0.7.0", "bitvec", @@ -8114,7 +8120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1008d95d2ec2d062959352527be30e10fec42a1aa5e5a48d990a5ff0fb9bdc0" dependencies = [ "anyhow", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -8197,7 +8203,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -8284,7 +8290,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "uuid 1.11.0", "whoami", @@ -8326,7 +8332,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "uuid 1.11.0", "whoami", @@ -8533,13 +8539,13 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "siphasher", - "sourcemap 9.0.1", + "sourcemap 9.1.0", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", "swc_visit", "tracing", - "unicode-width", + "unicode-width 0.1.14", "url", ] @@ -8597,7 +8603,7 @@ dependencies = [ "num-bigint", "once_cell", "serde", - "sourcemap 9.0.1", + "sourcemap 9.1.0", "swc_allocator", "swc_atoms", "swc_common", @@ -8902,7 +8908,7 @@ dependencies = [ "byteorder", "enum-as-inner", "libc", - "thiserror", + "thiserror 1.0.69", "walkdir", ] @@ -8993,7 +8999,7 @@ dependencies = [ "tantivy-stacker", "tantivy-tokenizer-api", "tempfile", - "thiserror", + "thiserror 1.0.69", "time", "uuid 1.11.0", "winapi", @@ -9139,18 +9145,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", ] [[package]] name = "thiserror-impl" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -9198,7 +9224,7 @@ dependencies = [ "pretty-hex", "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-rustls 0.24.1", "tokio-util", @@ -9324,7 +9350,7 @@ dependencies = [ "rayon", "schemars", "serde", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -9355,7 +9381,7 @@ dependencies = [ "serde", "serde_json", "spm_precompiled", - "thiserror", + "thiserror 1.0.69", "unicode-normalization-alignments", "unicode-segmentation", "unicode_categories", @@ -9457,7 +9483,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -9703,7 +9729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.69", "time", "tracing-subscriber", ] @@ -9853,7 +9879,7 @@ dependencies = [ "rand 0.8.5", "serde", "smallvec", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -9876,7 +9902,7 @@ dependencies = [ "resolv-conf", "serde", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "trust-dns-proto", @@ -9903,7 +9929,7 @@ dependencies = [ "native-tls", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] @@ -9954,7 +9980,7 @@ dependencies = [ "schemars", "serde_json", "syn 2.0.87", - "thiserror", + "thiserror 1.0.69", "unicode-ident", ] @@ -10116,6 +10142,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -10578,7 +10610,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "axum", @@ -10620,7 +10652,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "argon2", @@ -10705,7 +10737,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.421.2" +version = "1.423.2" dependencies = [ "base64 0.22.1", "chrono", @@ -10723,7 +10755,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.421.2" +version = "1.423.2" dependencies = [ "chrono", "serde", @@ -10736,7 +10768,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "rsmq_async", @@ -10751,7 +10783,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "async-stream", @@ -10783,7 +10815,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "sqlx", - "thiserror", + "thiserror 1.0.69", "tikv-jemalloc-ctl", "tokio", "tracing", @@ -10797,7 +10829,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.421.2" +version = "1.423.2" dependencies = [ "regex", "rsmq_async", @@ -10812,7 +10844,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "bytes", @@ -10834,7 +10866,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.421.2" +version = "1.423.2" dependencies = [ "itertools 0.13.0", "lazy_static", @@ -10846,7 +10878,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.421.2" +version = "1.423.2" dependencies = [ "convert_case 0.6.0", "serde", @@ -10855,7 +10887,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10867,7 +10899,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "gosyn", @@ -10879,7 +10911,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10891,7 +10923,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10902,7 +10934,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10913,7 +10945,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "async-recursion", @@ -10931,7 +10963,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -10948,7 +10980,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10960,7 +10992,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "lazy_static", @@ -10978,7 +11010,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -10999,7 +11031,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "serde_json", @@ -11009,7 +11041,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "async-recursion", @@ -11042,7 +11074,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.421.2" +version = "1.423.2" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -11052,7 +11084,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.421.2" +version = "1.423.2" dependencies = [ "anyhow", "async-recursion", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 8663582c19..800e165e33 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.421.2" +version = "1.423.2" authors.workspace = true edition.workspace = true @@ -29,7 +29,7 @@ members = [ ] [workspace.package] -version = "1.421.2" +version = "1.423.2" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 6a33fdaadd..f3e6e24f4e 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -6534b0f31fb4a75dd706fca2ce91e37e77e4ad02 \ No newline at end of file +e3df64af864f9d3546a97ff21f719c553e34ff1c \ No newline at end of file diff --git a/backend/migrations/20241105225151_alert_add_acknowledged.down.sql b/backend/migrations/20241105225151_alert_add_acknowledged.down.sql new file mode 100644 index 0000000000..ed9a1323d6 --- /dev/null +++ b/backend/migrations/20241105225151_alert_add_acknowledged.down.sql @@ -0,0 +1 @@ +ALTER TABLE alerts DROP COLUMN acknowledged; \ No newline at end of file diff --git a/backend/migrations/20241105225151_alert_add_acknowledged.up.sql b/backend/migrations/20241105225151_alert_add_acknowledged.up.sql new file mode 100644 index 0000000000..5174aec6e0 --- /dev/null +++ b/backend/migrations/20241105225151_alert_add_acknowledged.up.sql @@ -0,0 +1,9 @@ +-- Step 1: Add the new column 'acknowledged' to the 'alerts' table +ALTER TABLE alerts +ADD COLUMN acknowledged BOOLEAN DEFAULT NULL; + +-- Step 2: Update all existing rows to set 'acknowledged' to true +-- we don't want to pop up notifications to all users after migrations +-- but only show new alerts from the point of the upgrade +UPDATE alerts +SET acknowledged = TRUE; \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs index 61ee657d2c..6869a24647 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -36,7 +36,7 @@ use windmill_common::{ ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, - OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, @@ -78,7 +78,7 @@ use crate::monitor::{ reload_hub_base_url_setting, reload_job_default_timeout_setting, reload_jwt_secret_setting, reload_license_key, reload_npm_config_registry_setting, reload_pip_index_url_setting, reload_retention_period_setting, reload_scim_token_setting, reload_smtp_config, - reload_worker_config, + reload_worker_config, reload_critical_alert_mute_ui_setting, }; #[cfg(feature = "parquet")] @@ -817,6 +817,12 @@ Windmill Community Edition {GIT_VERSION} tracing::error!(error = %e, "Could not reload jwt secret setting"); } }, + CRITICAL_ALERT_MUTE_UI_SETTING => { + tracing::info!("Critical alert UI setting changed"); + if let Err(e) = reload_critical_alert_mute_ui_setting(&db).await { + tracing::error!(error = %e, "Could not reload critical alert UI setting"); + } + }, a @_ => { tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a); } diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index b6cbd55330..da811bb6ff 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -39,7 +39,7 @@ use windmill_common::{ EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING, - PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, @@ -55,7 +55,7 @@ use windmill_common::{ WORKER_GROUP, }, BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, - METRICS_DEBUG_ENABLED, METRICS_ENABLED, + METRICS_DEBUG_ENABLED, METRICS_ENABLED, CRITICAL_ALERT_MUTE_UI_ENABLED }; use windmill_queue::cancel_job; use windmill_worker::{ @@ -131,6 +131,10 @@ pub async fn initial_load( tracing::error!("Error loading expose debug metrics: {e:#}"); } + if let Err(e) = reload_critical_alert_mute_ui_setting(db).await { + tracing::error!("Error loading critical alert mute ui setting: {e:#}"); + } + if let Err(e) = load_tag_per_workspace_enabled(db).await { tracing::error!("Error loading default tag per workpsace: {e:#}"); } @@ -226,6 +230,17 @@ pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> { Ok(()) } +pub async fn reload_critical_alert_mute_ui_setting(db: &DB) -> error::Result<()> { + let mute = load_value_from_global_settings(db, CRITICAL_ALERT_MUTE_UI_SETTING).await; + match mute { + Ok(Some(serde_json::Value::Bool(t))) => { + CRITICAL_ALERT_MUTE_UI_ENABLED.store(t, Ordering::Relaxed); + } + _ => (), + }; + Ok(()) +} + pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> { let metrics_enabled = load_value_from_global_settings(db, EXPOSE_DEBUG_METRICS_SETTING).await; match metrics_enabled { diff --git a/backend/windmill-api/openapi-deref.json b/backend/windmill-api/openapi-deref.json index db789c2163..a6e225259a 100644 --- a/backend/windmill-api/openapi-deref.json +++ b/backend/windmill-api/openapi-deref.json @@ -1,7 +1,7 @@ { "openapi": "3.0.3", "info": { - "version": "1.409.0", + "version": "1.418.0", "title": "Windmill API", "contact": { "name": "Windmill Team", @@ -1182,6 +1182,115 @@ } } }, + "/settings/critical_alerts": { + "get": { + "summary": "Get all critical alerts", + "operationId": "getCriticalAlerts", + "tags": [ + "setting" + ], + "parameters": [ + { + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "default": 1, + "description": "The page number to retrieve (minimum value is 1)" + } + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer", + "default": 10, + "maximum": 100, + "description": "Number of alerts per page (maximum is 100)" + } + }, + { + "in": "query", + "name": "acknowledged", + "schema": { + "type": "boolean", + "nullable": true, + "description": "Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts" + } + } + ], + "responses": { + "200": { + "description": "Successfully retrieved all critical alerts", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CriticalAlert" + } + } + } + } + } + } + } + }, + "/settings/critical_alerts/{id}/acknowledge": { + "post": { + "summary": "Acknowledge a critical alert", + "operationId": "acknowledgeCriticalAlert", + "tags": [ + "setting" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "The ID of the critical alert to acknowledge" + } + ], + "responses": { + "200": { + "description": "Successfully acknowledged the critical alert", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "Critical alert acknowledged" + } + } + } + } + } + } + }, + "/settings/critical_alerts/acknowledge_all": { + "post": { + "summary": "Acknowledge all unacknowledged critical alerts", + "operationId": "acknowledgeAllCriticalAlerts", + "tags": [ + "setting" + ], + "responses": { + "200": { + "description": "Successfully acknowledged all unacknowledged critical alerts.", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "All unacknowledged critical alerts acknowledged" + } + } + } + } + } + } + }, "/settings/test_license_key": { "post": { "summary": "test license key", @@ -2239,6 +2348,14 @@ }, { "$ref": "#/components/parameters/PerPage" + }, + { + "name": "active_only", + "in": "query", + "description": "filter only active users", + "schema": { + "type": "boolean" + } } ], "responses": { @@ -3328,6 +3445,44 @@ } } }, + "/w/{workspace}/workspaces/used_triggers": { + "get": { + "summary": "get used triggers", + "operationId": "getUsedTriggers", + "tags": [ + "workspace" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + } + ], + "responses": { + "200": { + "description": "status", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "http_routes_used": { + "type": "boolean" + }, + "websocket_used": { + "type": "boolean" + } + }, + "required": [ + "http_routes_used", + "websocket_used" + ] + } + } + } + } + } + } + }, "/w/{workspace}/users/list": { "get": { "summary": "list users", @@ -6362,6 +6517,36 @@ } } }, + "/w/{workspace}/scripts/get_latest_version/{path}": { + "get": { + "summary": "get scripts's latest version (hash)", + "operationId": "getScriptLatestVersion", + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/ScriptPath" + } + ], + "tags": [ + "script" + ], + "responses": { + "200": { + "description": "Script version/hash", + "content": { + "application/json": { + "required": false, + "schema": { + "$ref": "#/components/schemas/ScriptHistory" + } + } + } + } + } + } + }, "/w/{workspace}/scripts/history_update/h/{hash}/p/{path}": { "post": { "summary": "update history of a script", @@ -7178,6 +7363,36 @@ } } }, + "/w/{workspace}/flows/get_latest_version/{path}": { + "get": { + "summary": "get flow's latest version", + "operationId": "getFlowLatestVersion", + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/ScriptPath" + } + ], + "tags": [ + "flow" + ], + "responses": { + "200": { + "description": "Flow version", + "content": { + "application/json": { + "required": false, + "schema": { + "$ref": "#/components/schemas/FlowVersion" + } + } + } + } + } + } + }, "/w/{workspace}/flows/get/v/{version}/p/{path}": { "get": { "summary": "get flow version", @@ -8096,6 +8311,36 @@ } } }, + "/w/{workspace}/apps/get_latest_version/{path}": { + "get": { + "summary": "get apps's latest version", + "operationId": "getAppLatestVersion", + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/ScriptPath" + } + ], + "tags": [ + "app" + ], + "responses": { + "200": { + "description": "App version", + "content": { + "application/json": { + "required": false, + "schema": { + "$ref": "#/components/schemas/AppHistory" + } + } + } + } + } + } + }, "/w/{workspace}/apps/history_update/a/{id}/v/{version}": { "post": { "summary": "update app history", @@ -11495,21 +11740,217 @@ } } }, - "/w/{workspace}/http_triggers/used": { - "get": { - "summary": "whether http triggers are used", - "operationId": "used", + "/w/{workspace}/websocket_triggers/create": { + "post": { + "summary": "create websocket trigger", + "operationId": "createWebsocketTrigger", "tags": [ - "http_trigger" + "websocket_trigger" ], "parameters": [ { "$ref": "#/components/parameters/WorkspaceId" } ], + "requestBody": { + "description": "new websocket trigger", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewWebsocketTrigger" + } + } + } + }, + "responses": { + "201": { + "description": "websocket trigger created", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/update/{path}": { + "post": { + "summary": "update websocket trigger", + "operationId": "updateWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "requestBody": { + "description": "updated trigger", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditWebsocketTrigger" + } + } + } + }, "responses": { "200": { - "description": "whether http triggers are used", + "description": "websocket trigger updated", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/delete/{path}": { + "delete": { + "summary": "delete websocket trigger", + "operationId": "deleteWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "responses": { + "200": { + "description": "websocket trigger deleted", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/get/{path}": { + "get": { + "summary": "get websocket trigger", + "operationId": "getWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "responses": { + "200": { + "description": "websocket trigger deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsocketTrigger" + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/list": { + "get": { + "summary": "list websocket triggers", + "operationId": "listWebsocketTriggers", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId", + "required": true + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/PerPage" + }, + { + "name": "path", + "description": "filter by path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "is_flow", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "path_start", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "websocket trigger list", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTrigger" + } + } + } + } + } + } + } + }, + "/w/{workspace}/websocket_triggers/exists/{path}": { + "get": { + "summary": "does websocket trigger exists", + "operationId": "existsWebsocketTrigger", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "responses": { + "200": { + "description": "websocket trigger exists", "content": { "application/json": { "schema": { @@ -11521,6 +11962,54 @@ } } }, + "/w/{workspace}/websocket_triggers/setenabled/{path}": { + "post": { + "summary": "set enabled websocket trigger", + "operationId": "setWebsocketTriggerEnabled", + "tags": [ + "websocket_trigger" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WorkspaceId" + }, + { + "$ref": "#/components/parameters/Path" + } + ], + "requestBody": { + "description": "updated websocket trigger enable", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ] + } + } + } + }, + "responses": { + "200": { + "description": "websocket trigger enabled set", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/groups/list": { "get": { "summary": "list instance groups", @@ -12812,6 +13301,40 @@ } } }, + "/configs/list_autoscaling_events/{worker_group}": { + "get": { + "summary": "List autoscaling events", + "operationId": "listAutoscalingEvents", + "tags": [ + "config" + ], + "parameters": [ + { + "name": "worker_group", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "List of autoscaling events", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AutoscalingEvent" + } + } + } + } + } + } + } + }, "/w/{workspace}/acls/get/{kind}/{path}": { "get": { "summary": "get granular acls", @@ -12842,7 +13365,8 @@ "folder", "app", "raw_app", - "http_trigger" + "http_trigger", + "websocket_trigger" ] } } @@ -12894,7 +13418,8 @@ "folder", "app", "raw_app", - "http_trigger" + "http_trigger", + "websocket_trigger" ] } } @@ -12965,7 +13490,8 @@ "folder", "app", "raw_app", - "http_trigger" + "http_trigger", + "websocket_trigger" ] } } @@ -14959,6 +15485,165 @@ } } } + }, + "/srch/index/search/service_logs": { + "get": { + "summary": "Search through service logs with a string query", + "operationId": "searchLogsIndex", + "tags": [ + "indexSearch" + ], + "parameters": [ + { + "name": "search_query", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "mode", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "worker_group", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "hostname", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "min_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "max_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "search results", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query_parse_errors": { + "description": "a list of the terms that couldn't be parsed (and thus ignored)", + "type": "array", + "items": { + "type": "string" + } + }, + "hits": { + "description": "log files that matched the query", + "type": "array", + "items": { + "$ref": "#/components/schemas/LogSearchHit" + } + } + } + } + } + } + } + } + } + }, + "/srch/index/search/count_service_logs": { + "get": { + "summary": "Search and count the log line hits on every provided host", + "operationId": "countSearchLogsIndex", + "tags": [ + "indexSearch" + ], + "parameters": [ + { + "name": "search_query", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "hosts", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "min_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "max_ts", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "search results", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query_parse_errors": { + "description": "a list of the terms that couldn't be parsed (and thus ignored)", + "type": "array", + "items": { + "type": "string" + } + }, + "count_per_host": { + "description": "count of log lines that matched the query per hostname", + "type": "object" + } + } + } + } + } + } + } + } } }, "components": { @@ -16560,6 +17245,7 @@ "users.delete", "users.update", "users.login", + "users.login_failure", "users.logout", "users.accept_invite", "users.decline_invite", @@ -16571,6 +17257,7 @@ "users.impersonate", "users.leave_workspace", "oauth.login", + "oauth.login_failure", "oauth.signup", "variables.create", "variables.delete", @@ -17533,9 +18220,248 @@ }, "email_count": { "type": "number" + }, + "websocket_count": { + "type": "number" } } }, + "WebsocketTrigger": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "edited_by": { + "type": "string" + }, + "edited_at": { + "type": "string", + "format": "date-time" + }, + "script_path": { + "type": "string" + }, + "url": { + "type": "string" + }, + "is_flow": { + "type": "boolean" + }, + "extra_perms": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "email": { + "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "server_id": { + "type": "string" + }, + "last_server_ping": { + "type": "string", + "format": "date-time" + }, + "error": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ] + } + }, + "initial_messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTriggerInitialMessage" + } + }, + "url_runnable_args": { + "$ref": "#/components/schemas/ScriptArgs" + } + }, + "required": [ + "path", + "edited_by", + "edited_at", + "script_path", + "url", + "extra_perms", + "is_flow", + "email", + "workspace_id", + "enabled", + "filters", + "initial_messages", + "url_runnable_args" + ] + }, + "NewWebsocketTrigger": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "script_path": { + "type": "string" + }, + "is_flow": { + "type": "boolean" + }, + "url": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ] + } + }, + "initial_messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTriggerInitialMessage" + } + }, + "url_runnable_args": { + "$ref": "#/components/schemas/ScriptArgs" + } + }, + "required": [ + "path", + "script_path", + "url", + "is_flow", + "filters", + "initial_messages", + "url_runnable_args" + ] + }, + "EditWebsocketTrigger": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "path": { + "type": "string" + }, + "script_path": { + "type": "string" + }, + "is_flow": { + "type": "boolean" + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ] + } + }, + "initial_messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsocketTriggerInitialMessage" + } + }, + "url_runnable_args": { + "$ref": "#/components/schemas/ScriptArgs" + } + }, + "required": [ + "path", + "script_path", + "url", + "is_flow", + "filters", + "initial_messages", + "url_runnable_args" + ] + }, + "WebsocketTriggerInitialMessage": { + "anyOf": [ + { + "type": "object", + "properties": { + "raw_message": { + "type": "string" + } + }, + "required": [ + "raw_message" + ] + }, + { + "type": "object", + "properties": { + "runnable_result": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "args": { + "$ref": "#/components/schemas/ScriptArgs" + }, + "is_flow": { + "type": "boolean" + } + }, + "required": [ + "path", + "args", + "is_flow" + ] + } + }, + "required": [ + "runnable_result" + ] + } + ] + }, "Group": { "type": "object", "properties": { @@ -17817,6 +18743,9 @@ }, "username": { "type": "string" + }, + "operator_only": { + "type": "boolean" } }, "required": [ @@ -18796,6 +19725,66 @@ } } }, + "LogSearchHit": { + "type": "object", + "properties": { + "dancer": { + "type": "string" + } + } + }, + "AutoscalingEvent": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "worker_group": { + "type": "string" + }, + "event_type": { + "type": "string" + }, + "desired_workers": { + "type": "integer" + }, + "reason": { + "type": "string" + }, + "applied_at": { + "type": "string", + "format": "date-time" + } + } + }, + "CriticalAlert": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Unique identifier for the alert" + }, + "alert_type": { + "type": "string", + "description": "Type of alert (e.g., critical_error)" + }, + "message": { + "type": "string", + "description": "The message content of the alert" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Time when the alert was created" + }, + "acknowledged": { + "type": "boolean", + "nullable": true, + "description": "Acknowledgment status of the alert, can be true, false, or null if not set" + } + } + }, "StaticTransform": { "type": "object", "properties": { @@ -18901,6 +19890,9 @@ }, "custom_concurrency_key": { "type": "string" + }, + "is_trigger": { + "type": "boolean" } }, "required": [ @@ -18933,6 +19925,9 @@ }, "tag_override": { "type": "string" + }, + "is_trigger": { + "type": "boolean" } }, "required": [ diff --git a/backend/windmill-api/openapi-deref.yaml b/backend/windmill-api/openapi-deref.yaml index e2a0b00a85..94206795c2 100644 --- a/backend/windmill-api/openapi-deref.yaml +++ b/backend/windmill-api/openapi-deref.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 1.409.0 + version: 1.418.0 title: Windmill API contact: name: Windmill Team @@ -142,6 +142,7 @@ paths: - users.delete - users.update - users.login + - users.login_failure - users.logout - users.accept_invite - users.decline_invite @@ -153,6 +154,7 @@ paths: - users.impersonate - users.leave_workspace - oauth.login + - oauth.login_failure - oauth.signup - variables.create - variables.delete @@ -231,24 +233,24 @@ paths: - name: before description: filter on started before (inclusive) timestamp in: query - schema: &ref_109 + schema: &ref_118 type: string format: date-time - name: after description: filter on created after (exclusive) timestamp in: query - schema: &ref_110 + schema: &ref_119 type: string format: date-time - name: username description: filter on exact username of user in: query - schema: &ref_119 + schema: &ref_128 type: string - name: operation description: filter on exact or prefix name of operation in: query - schema: &ref_120 + schema: &ref_129 type: string - name: operations in: query @@ -263,12 +265,12 @@ paths: - name: resource description: filter on exact or prefix name of resource in: query - schema: &ref_121 + schema: &ref_130 type: string - name: action_kind description: filter on type of operation in: query - schema: &ref_122 + schema: &ref_131 type: string enum: - Create @@ -300,12 +302,12 @@ paths: application/json: schema: type: object - properties: &ref_136 + properties: &ref_143 email: type: string password: type: string - required: &ref_137 + required: &ref_144 - email - password responses: @@ -428,7 +430,7 @@ paths: application/json: schema: type: object - properties: &ref_138 + properties: &ref_145 is_admin: type: boolean operator: @@ -782,7 +784,7 @@ paths: application/json: schema: type: object - properties: &ref_172 + properties: &ref_183 email: type: string workspaces: @@ -800,7 +802,7 @@ paths: - id - name - username - required: &ref_173 + required: &ref_184 - email - workspaces /workspaces/list_as_superadmin: @@ -842,14 +844,14 @@ paths: application/json: schema: type: object - properties: &ref_174 + properties: &ref_185 id: type: string name: type: string username: type: string - required: &ref_175 + required: &ref_186 - id - name responses: @@ -1042,6 +1044,98 @@ paths: text/plain: schema: type: string + /settings/critical_alerts: + get: + summary: Get all critical alerts + operationId: getCriticalAlerts + tags: + - setting + parameters: + - in: query + name: page + schema: + type: integer + default: 1 + description: The page number to retrieve (minimum value is 1) + - in: query + name: page_size + schema: + type: integer + default: 10 + maximum: 100 + description: Number of alerts per page (maximum is 100) + - in: query + name: acknowledged + schema: + type: boolean + nullable: true + description: >- + Filter by acknowledgment status; true for acknowledged, false for + unacknowledged, and omit for all alerts + responses: + '200': + description: Successfully retrieved all critical alerts + content: + application/json: + schema: + type: array + items: + type: object + properties: &ref_228 + id: + type: integer + description: Unique identifier for the alert + alert_type: + type: string + description: Type of alert (e.g., critical_error) + message: + type: string + description: The message content of the alert + created_at: + type: string + format: date-time + description: Time when the alert was created + acknowledged: + type: boolean + nullable: true + description: >- + Acknowledgment status of the alert, can be true, false, + or null if not set + /settings/critical_alerts/{id}/acknowledge: + post: + summary: Acknowledge a critical alert + operationId: acknowledgeCriticalAlert + tags: + - setting + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The ID of the critical alert to acknowledge + responses: + '200': + description: Successfully acknowledged the critical alert + content: + application/json: + schema: + type: string + example: Critical alert acknowledged + /settings/critical_alerts/acknowledge_all: + post: + summary: Acknowledge all unacknowledged critical alerts + operationId: acknowledgeAllCriticalAlerts + tags: + - setting + responses: + '200': + description: Successfully acknowledged all unacknowledged critical alerts. + content: + application/json: + schema: + type: string + example: All unacknowledged critical alerts acknowledged /settings/test_license_key: post: summary: test license key @@ -1197,12 +1291,12 @@ paths: type: array items: type: object - properties: &ref_214 + properties: &ref_221 name: type: string value: type: object - required: &ref_215 + required: &ref_222 - name - value /users/email: @@ -1361,6 +1455,8 @@ paths: type: string username: type: string + operator_only: + type: boolean required: &ref_13 - email - login_type @@ -1784,6 +1880,11 @@ paths: description: number of items to return for a given page (default 30, max 100) in: query schema: *ref_6 + - name: active_only + in: query + description: filter only active users + schema: + type: boolean responses: '200': description: user @@ -1932,7 +2033,7 @@ paths: type: array items: type: object - properties: &ref_198 + properties: &ref_205 script_path: type: string git_repo_resource_path: @@ -1957,7 +2058,7 @@ paths: - schedule - user - group - required: &ref_199 + required: &ref_206 - script_path - git_repo_resource_path deploy_ui: @@ -2639,6 +2740,32 @@ paths: text/plain: schema: type: number + /w/{workspace}/workspaces/used_triggers: + get: + summary: get used triggers + operationId: getUsedTriggers + tags: + - workspace + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + responses: + '200': + description: status + content: + application/json: + schema: + type: object + properties: + http_routes_used: + type: boolean + websocket_used: + type: boolean + required: + - http_routes_used + - websocket_used /w/{workspace}/users/list: get: summary: list users @@ -2681,7 +2808,7 @@ paths: type: array items: type: object - properties: &ref_135 + properties: &ref_142 email: type: string executions: @@ -2742,7 +2869,7 @@ paths: application/json: schema: type: object - properties: &ref_139 + properties: &ref_146 label: type: string expiration: @@ -2774,7 +2901,7 @@ paths: application/json: schema: type: object - properties: &ref_140 + properties: &ref_147 label: type: string expiration: @@ -2784,7 +2911,7 @@ paths: type: string workspace_id: type: string - required: &ref_141 + required: &ref_148 - impersonate_email responses: '201': @@ -2909,7 +3036,7 @@ paths: application/json: schema: type: object - properties: &ref_144 + properties: &ref_151 path: type: string value: @@ -2925,7 +3052,7 @@ paths: expires_at: type: string format: date-time - required: &ref_145 + required: &ref_152 - path - value - is_secret @@ -3010,7 +3137,7 @@ paths: application/json: schema: type: object - properties: &ref_146 + properties: &ref_153 path: type: string value: @@ -3195,7 +3322,7 @@ paths: type: array items: type: object - properties: &ref_142 + properties: &ref_149 name: type: string value: @@ -3204,7 +3331,7 @@ paths: type: string is_custom: type: boolean - required: &ref_143 + required: &ref_150 - name - value - description @@ -3344,7 +3471,7 @@ paths: application/json: schema: type: object - properties: &ref_191 + properties: &ref_198 access_token: type: string expires_in: @@ -3355,7 +3482,7 @@ paths: type: array items: type: string - required: &ref_192 + required: &ref_199 - access_token /w/{workspace}/oauth/create_account: post: @@ -3560,7 +3687,7 @@ paths: application/json: schema: type: object - properties: &ref_153 + properties: &ref_160 path: type: string value: {} @@ -3568,7 +3695,7 @@ paths: type: string resource_type: type: string - required: &ref_154 + required: &ref_161 - path - value - resource_type @@ -3623,7 +3750,7 @@ paths: application/json: schema: type: object - properties: &ref_155 + properties: &ref_162 path: type: string description: @@ -3689,7 +3816,7 @@ paths: application/json: schema: type: object - properties: &ref_156 + properties: &ref_163 workspace_id: type: string path: @@ -3710,7 +3837,7 @@ paths: edited_at: type: string format: date-time - required: &ref_157 + required: &ref_164 - path - resource_type - is_oauth @@ -3826,7 +3953,7 @@ paths: type: array items: type: object - properties: &ref_158 + properties: &ref_165 workspace_id: type: string path: @@ -3857,7 +3984,7 @@ paths: edited_at: type: string format: date-time - required: &ref_159 + required: &ref_166 - path - resource_type - is_oauth @@ -3904,7 +4031,7 @@ paths: - name: name in: path required: true - schema: &ref_92 + schema: &ref_101 type: string responses: '200': @@ -4026,7 +4153,7 @@ paths: application/json: schema: type: object - properties: &ref_160 + properties: &ref_167 schema: {} description: type: string @@ -4254,14 +4381,14 @@ paths: properties: flow: type: object - properties: &ref_50 + properties: &ref_52 summary: type: string description: type: string value: type: object - properties: &ref_58 + properties: &ref_64 modules: type: array items: @@ -4270,32 +4397,32 @@ paths: id: type: string value: - oneOf: &ref_239 + oneOf: &ref_249 - type: object - properties: &ref_223 + properties: &ref_233 input_transforms: type: object additionalProperties: oneOf: &ref_29 - type: object - properties: &ref_219 + properties: &ref_229 value: {} type: type: string enum: - javascript - required: &ref_220 + required: &ref_230 - expr - type - type: object - properties: &ref_221 + properties: &ref_231 expr: type: string type: type: string enum: - javascript - required: &ref_222 + required: &ref_232 - expr - type discriminator: &ref_30 @@ -4338,13 +4465,15 @@ paths: type: number custom_concurrency_key: type: string - required: &ref_224 + is_trigger: + type: boolean + required: &ref_234 - type - content - language - input_transforms - type: object - properties: &ref_225 + properties: &ref_235 input_transforms: type: object additionalProperties: @@ -4360,12 +4489,14 @@ paths: - script tag_override: type: string - required: &ref_226 + is_trigger: + type: boolean + required: &ref_236 - type - path - input_transforms - type: object - properties: &ref_227 + properties: &ref_237 input_transforms: type: object additionalProperties: @@ -4377,12 +4508,12 @@ paths: type: string enum: - flow - required: &ref_228 + required: &ref_238 - type - path - input_transforms - type: object - properties: &ref_229 + properties: &ref_239 modules: type: array items: @@ -4404,13 +4535,13 @@ paths: type: boolean parallelism: type: integer - required: &ref_230 + required: &ref_240 - modules - iterator - skip_failures - type - type: object - properties: &ref_231 + properties: &ref_241 modules: type: array items: @@ -4427,12 +4558,12 @@ paths: type: boolean parallelism: type: integer - required: &ref_232 + required: &ref_242 - modules - skip_failures - type - type: object - properties: &ref_233 + properties: &ref_243 branches: type: array items: @@ -4463,12 +4594,12 @@ paths: type: string enum: - branchone - required: &ref_234 + required: &ref_244 - branches - default - type - type: object - properties: &ref_235 + properties: &ref_245 branches: type: array items: @@ -4493,20 +4624,20 @@ paths: - branchall parallel: type: boolean - required: &ref_236 + required: &ref_246 - branches - type - type: object - properties: &ref_237 + properties: &ref_247 type: type: string enum: - identity flow: type: boolean - required: &ref_238 + required: &ref_248 - type - discriminator: &ref_240 + discriminator: &ref_250 propertyName: type mapping: rawscript: '#/components/schemas/RawScript' @@ -4588,7 +4719,7 @@ paths: type: boolean retry: type: object - properties: &ref_87 + properties: &ref_93 constant: type: object properties: @@ -4634,11 +4765,11 @@ paths: type: number early_return: type: string - required: &ref_59 + required: &ref_65 - modules schema: type: object - required: &ref_51 + required: &ref_53 - summary - value /apps/hub/list: @@ -4945,12 +5076,12 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: &ref_48 + schema: &ref_50 type: boolean - name: created_by description: mask to filter exact matching user creator in: query - schema: &ref_49 + schema: &ref_51 type: string - name: path_start description: mask to filter matching starting path @@ -5611,7 +5742,7 @@ paths: application/json: schema: type: object - properties: &ref_53 + properties: &ref_57 primary_schedule: type: object properties: @@ -5625,6 +5756,8 @@ paths: type: number email_count: type: number + websocket_count: + type: number /w/{workspace}/scripts/list_tokens/{path}: get: summary: get tokens with script scope @@ -5672,7 +5805,7 @@ paths: content: application/json: schema: - allOf: &ref_126 + allOf: &ref_135 - type: object properties: *ref_40 required: *ref_41 @@ -5710,13 +5843,38 @@ paths: type: array items: type: object - properties: &ref_127 + properties: &ref_42 script_hash: type: string deployment_msg: type: string - required: &ref_128 + required: &ref_43 - script_hash + /w/{workspace}/scripts/get_latest_version/{path}: + get: + summary: get scripts's latest version (hash) + operationId: getScriptLatestVersion + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_33 + tags: + - script + responses: + '200': + description: Script version/hash + content: + application/json: + required: false + schema: + type: object + properties: *ref_42 + required: *ref_43 /w/{workspace}/scripts/history_update/h/{hash}/p/{path}: post: summary: update history of a script @@ -5791,7 +5949,7 @@ paths: - name: token in: path required: true - schema: &ref_115 + schema: &ref_124 type: string - name: path in: path @@ -5939,20 +6097,20 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: &ref_42 + schema: &ref_44 type: string format: uuid - name: tag description: Override the tag to use in: query - schema: &ref_44 + schema: &ref_46 type: string - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: &ref_45 + schema: &ref_47 type: string - name: job_id description: >- @@ -5961,7 +6119,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: &ref_43 + schema: &ref_45 type: string format: uuid - name: invisible_to_owner @@ -6005,7 +6163,7 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6013,7 +6171,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -6022,14 +6180,14 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: &ref_46 + schema: &ref_48 type: string - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: &ref_47 + schema: &ref_49 type: string requestBody: description: script args @@ -6065,17 +6223,17 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6083,7 +6241,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -6092,13 +6250,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 requestBody: description: script args required: true @@ -6132,17 +6290,17 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6150,7 +6308,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -6159,13 +6317,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 - name: payload description: > The base64 encoded payload that has been encoded as a JSON. e.g how @@ -6173,7 +6331,7 @@ paths: `encodeURIComponent(btoa(JSON.stringify({a: 2})))` in: query - schema: &ref_86 + schema: &ref_92 type: string responses: '200': @@ -6204,13 +6362,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6218,7 +6376,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: script args required: true @@ -6256,13 +6414,13 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -6270,7 +6428,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: script args required: true @@ -6381,11 +6539,11 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: path_start description: mask to filter matching starting path in: query @@ -6439,12 +6597,12 @@ paths: type: array items: allOf: - - allOf: &ref_52 + - allOf: &ref_56 - type: object - properties: *ref_50 - required: *ref_51 + properties: *ref_52 + required: *ref_53 - type: object - properties: &ref_177 + properties: &ref_188 workspace_id: type: string path: @@ -6458,7 +6616,7 @@ paths: type: boolean extra_perms: type: object - additionalProperties: &ref_176 + additionalProperties: &ref_187 type: boolean starred: type: boolean @@ -6476,7 +6634,7 @@ paths: type: number visible_to_runner_only: type: boolean - required: &ref_178 + required: &ref_189 - path - edited_by - edited_at @@ -6512,7 +6670,7 @@ paths: type: array items: type: object - properties: &ref_189 + properties: &ref_54 id: type: integer created_at: @@ -6520,9 +6678,34 @@ paths: format: date-time deployment_msg: type: string - required: &ref_190 + required: &ref_55 - id - created_at + /w/{workspace}/flows/get_latest_version/{path}: + get: + summary: get flow's latest version + operationId: getFlowLatestVersion + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_33 + tags: + - flow + responses: + '200': + description: Flow version + content: + application/json: + required: false + schema: + type: object + properties: *ref_54 + required: *ref_55 /w/{workspace}/flows/get/v/{version}/p/{path}: get: summary: get flow version @@ -6550,7 +6733,7 @@ paths: content: application/json: schema: - allOf: *ref_52 + allOf: *ref_56 /w/{workspace}/flows/history_update/v/{version}/p/{path}: post: summary: update flow history @@ -6616,7 +6799,7 @@ paths: content: application/json: schema: - allOf: *ref_52 + allOf: *ref_56 /w/{workspace}/flows/get_triggers_count/{path}: get: summary: get triggers count of flow @@ -6639,7 +6822,7 @@ paths: application/json: schema: type: object - properties: *ref_53 + properties: *ref_57 /w/{workspace}/flows/list_tokens/{path}: get: summary: get tokens with flow scope @@ -6720,11 +6903,11 @@ paths: application/json: schema: allOf: - - allOf: *ref_52 + - allOf: *ref_56 - type: object properties: draft: - allOf: *ref_52 + allOf: *ref_56 /w/{workspace}/flows/exists/{path}: get: summary: exists flow by path @@ -6765,10 +6948,10 @@ paths: application/json: schema: allOf: - - allOf: &ref_54 + - allOf: &ref_58 - type: object - properties: *ref_50 - required: *ref_51 + properties: *ref_52 + required: *ref_53 - type: object properties: path: @@ -6822,7 +7005,7 @@ paths: application/json: schema: allOf: - - allOf: *ref_54 + - allOf: *ref_58 - type: object properties: deployment_message: @@ -6910,11 +7093,11 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: path_start description: mask to filter matching starting path in: query @@ -6941,7 +7124,7 @@ paths: type: array items: type: object - properties: &ref_184 + properties: &ref_195 workspace_id: type: string path: @@ -6959,7 +7142,7 @@ paths: edited_at: type: string format: date-time - required: &ref_185 + required: &ref_196 - workspace_id - path - summary @@ -7002,7 +7185,7 @@ paths: - name: version in: path required: true - schema: &ref_114 + schema: &ref_123 type: number - name: path in: path @@ -7064,11 +7247,11 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: path_start description: mask to filter matching starting path in: query @@ -7109,7 +7292,7 @@ paths: type: array items: type: object - properties: &ref_182 + properties: &ref_193 id: type: integer workspace_id: @@ -7135,7 +7318,7 @@ paths: - viewer - publisher - anonymous - required: &ref_183 + required: &ref_194 - id - workspace_id - path @@ -7170,7 +7353,7 @@ paths: type: string policy: type: object - properties: &ref_55 + properties: &ref_59 triggerables: type: object additionalProperties: @@ -7253,7 +7436,7 @@ paths: application/json: schema: type: object - properties: &ref_56 + properties: &ref_60 id: type: integer workspace_id: @@ -7275,7 +7458,7 @@ paths: type: object policy: type: object - properties: *ref_55 + properties: *ref_59 execution_mode: type: string enum: @@ -7286,7 +7469,7 @@ paths: type: object additionalProperties: type: boolean - required: &ref_57 + required: &ref_61 - id - workspace_id - path @@ -7319,10 +7502,10 @@ paths: content: application/json: schema: - allOf: &ref_186 + allOf: &ref_197 - type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 - type: object properties: draft_only: @@ -7352,13 +7535,38 @@ paths: type: array items: type: object - properties: &ref_187 + properties: &ref_62 version: type: integer deployment_msg: type: string - required: &ref_188 + required: &ref_63 - version + /w/{workspace}/apps/get_latest_version/{path}: + get: + summary: get apps's latest version + operationId: getAppLatestVersion + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_33 + tags: + - app + responses: + '200': + description: App version + content: + application/json: + required: false + schema: + type: object + properties: *ref_62 + required: *ref_63 /w/{workspace}/apps/history_update/a/{id}/v/{version}: post: summary: update app history @@ -7377,7 +7585,7 @@ paths: - name: version in: path required: true - schema: &ref_116 + schema: &ref_125 type: integer requestBody: description: App deployment message @@ -7418,8 +7626,8 @@ paths: application/json: schema: type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 /w/{workspace}/apps_u/public_resource/{path}: get: summary: get public resource @@ -7485,8 +7693,8 @@ paths: application/json: schema: type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 /w/{workspace}/raw_apps/create: post: summary: create raw app @@ -7633,7 +7841,7 @@ paths: value: {} policy: type: object - properties: *ref_55 + properties: *ref_59 deployment_message: type: string responses: @@ -7741,11 +7949,11 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -7753,7 +7961,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -7762,7 +7970,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the flow owner (default false) in: query @@ -7798,7 +8006,7 @@ paths: - name: id in: path required: true - schema: &ref_83 + schema: &ref_89 type: string format: uuid - name: step_id @@ -7831,11 +8039,11 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -7843,7 +8051,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -7852,7 +8060,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the flow owner (default false) in: query @@ -7910,17 +8118,17 @@ paths: The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 - name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 - name: job_id description: >- The job id to assign to the created job. if missing, job is chosen @@ -7928,7 +8136,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 - name: include_header description: > List of headers's keys (separated with ',') whove value are added to @@ -7937,7 +8145,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the script owner (default false) in: query @@ -7977,7 +8185,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the script owner (default false) in: query @@ -7990,7 +8198,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: preview required: true @@ -7998,7 +8206,7 @@ paths: application/json: schema: type: object - properties: &ref_147 + properties: &ref_154 content: type: string path: @@ -8037,7 +8245,7 @@ paths: type: boolean lock: type: string - required: &ref_148 + required: &ref_155 - args responses: '201': @@ -8075,11 +8283,11 @@ paths: application/json: schema: type: object - properties: &ref_149 + properties: &ref_156 args: type: object additionalProperties: *ref_16 - required: &ref_150 + required: &ref_157 - args responses: '201': @@ -8112,7 +8320,7 @@ paths: type: array items: type: object - properties: &ref_208 + properties: &ref_215 raw_code: type: string path: @@ -8136,7 +8344,7 @@ paths: - php - rust - ansible - required: &ref_209 + required: &ref_216 - raw_code - path - language @@ -8176,7 +8384,7 @@ paths: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 - name: invisible_to_owner description: make the run invisible to the the script owner (default false) in: query @@ -8189,7 +8397,7 @@ paths: queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 requestBody: description: preview required: true @@ -8197,11 +8405,11 @@ paths: application/json: schema: type: object - properties: &ref_179 + properties: &ref_190 value: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 path: type: string args: @@ -8211,7 +8419,7 @@ paths: type: string restarted_from: type: object - properties: &ref_181 + properties: &ref_192 flow_job_id: type: string format: uuid @@ -8219,7 +8427,7 @@ paths: type: string branch_or_iteration_n: type: integer - required: &ref_180 + required: &ref_191 - value - content - args @@ -8245,94 +8453,94 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: &ref_62 + schema: &ref_68 type: string - name: script_path_start description: mask to filter matching starting path in: query - schema: &ref_63 + schema: &ref_69 type: string - name: schedule_path description: mask to filter by schedule path in: query - schema: &ref_64 + schema: &ref_70 type: string - name: script_hash description: mask to filter exact matching path in: query - schema: &ref_65 + schema: &ref_71 type: string - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: &ref_66 + schema: &ref_72 type: string format: date-time - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: &ref_67 + schema: &ref_73 type: string format: date-time - name: success description: filter on successful jobs in: query - schema: &ref_68 + schema: &ref_74 type: boolean - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: &ref_69 + schema: &ref_75 type: boolean - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: &ref_70 + schema: &ref_76 type: string - name: suspended description: filter on suspended jobs in: query - schema: &ref_71 + schema: &ref_77 type: boolean - name: running description: filter on running jobs in: query - schema: &ref_72 + schema: &ref_78 type: boolean - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: &ref_73 + schema: &ref_79 type: string - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: &ref_74 + schema: &ref_80 type: string - name: tag description: filter on jobs with a given tag/worker group in: query - schema: &ref_75 + schema: &ref_81 type: string - name: page description: which page to return (start at 1, default 1) @@ -8363,7 +8571,7 @@ paths: type: array items: type: object - properties: &ref_81 + properties: &ref_87 workspace_id: type: string id: @@ -8430,14 +8638,14 @@ paths: by extension its DT_TOKEN. flow_status: type: object - properties: &ref_76 + properties: &ref_82 step: type: integer modules: type: array items: type: object - properties: &ref_60 + properties: &ref_66 type: type: string enum: @@ -8514,20 +8722,20 @@ paths: format: uuid skipped: type: boolean - required: &ref_61 + required: &ref_67 - type user_states: additionalProperties: true preprocessor_module: allOf: - type: object - properties: *ref_60 - required: *ref_61 + properties: *ref_66 + required: *ref_67 failure_module: allOf: - type: object - properties: *ref_60 - required: *ref_61 + properties: *ref_66 + required: *ref_67 - type: object properties: parent_module: @@ -8542,14 +8750,14 @@ paths: items: type: string format: uuid - required: &ref_77 + required: &ref_83 - step - modules - failure_module raw_flow: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 is_flow_step: type: boolean language: @@ -8587,7 +8795,7 @@ paths: type: number suspend: type: number - required: &ref_82 + required: &ref_88 - id - running - canceled @@ -8666,79 +8874,79 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: success description: filter on successful jobs in: query - schema: *ref_68 + schema: *ref_74 - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: suspended description: filter on suspended jobs in: query - schema: *ref_71 + schema: *ref_77 - name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: page description: which page to return (start at 1, default 1) in: query @@ -8816,75 +9024,75 @@ paths: - name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: label description: >- mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: &ref_78 + schema: &ref_84 type: string - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: success description: filter on successful jobs in: query - schema: *ref_68 + schema: *ref_74 - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: page description: which page to return (start at 1, default 1) in: query @@ -8922,7 +9130,7 @@ paths: type: array items: type: object - properties: &ref_79 + properties: &ref_85 workspace_id: type: string id: @@ -8988,12 +9196,12 @@ paths: by extension its DT_TOKEN. flow_status: type: object - properties: *ref_76 - required: *ref_77 + properties: *ref_82 + required: *ref_83 raw_flow: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 is_flow_step: type: boolean language: @@ -9035,7 +9243,7 @@ paths: type: number aggregate_wait_time_ms: type: number - required: &ref_80 + required: &ref_86 - id - created_by - duration_ms @@ -9064,54 +9272,54 @@ paths: - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: label description: >- mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: *ref_78 + schema: *ref_84 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: created_before description: filter on created before (inclusive) timestamp in: query - schema: &ref_117 + schema: &ref_126 type: string format: date-time - name: created_after description: filter on created after (exclusive) timestamp in: query - schema: &ref_118 + schema: &ref_127 type: string format: date-time - name: created_or_started_before @@ -9119,23 +9327,23 @@ paths: filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query - schema: &ref_111 + schema: &ref_120 type: string format: date-time - name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 - name: created_or_started_after description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query - schema: &ref_112 + schema: &ref_121 type: string format: date-time - name: created_or_started_after_completed_jobs @@ -9144,7 +9352,7 @@ paths: otherwise after (exclusive) timestamp but only for the completed jobs in: query - schema: &ref_113 + schema: &ref_122 type: string format: date-time - name: job_kinds @@ -9152,27 +9360,27 @@ paths: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: suspended description: filter on suspended jobs in: query - schema: *ref_71 + schema: *ref_77 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: page description: which page to return (start at 1, default 1) in: query @@ -9221,11 +9429,11 @@ paths: schema: type: array items: - oneOf: &ref_84 + oneOf: &ref_90 - allOf: - type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 - type: object properties: type: @@ -9234,15 +9442,15 @@ paths: - CompletedJob - allOf: - type: object - properties: *ref_81 - required: *ref_82 + properties: *ref_87 + required: *ref_88 - type: object properties: type: type: string enum: - QueuedJob - discriminator: &ref_85 + discriminator: &ref_91 propertyName: type /jobs/db_clock: get: @@ -9309,7 +9517,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: no_logs in: query schema: @@ -9320,8 +9528,8 @@ paths: content: application/json: schema: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 /w/{workspace}/jobs_u/get_root_job_id/{id}: get: summary: get root job id @@ -9336,7 +9544,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: get root job id @@ -9358,7 +9566,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job details @@ -9380,7 +9588,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job args @@ -9401,7 +9609,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: running in: query schema: @@ -9436,9 +9644,9 @@ paths: type: integer flow_status: type: object - additionalProperties: &ref_151 + additionalProperties: &ref_158 type: object - properties: &ref_152 + properties: &ref_159 scheduled_for: type: string format: date-time @@ -9485,7 +9693,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: flow debug info details @@ -9506,7 +9714,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job details @@ -9514,8 +9722,8 @@ paths: application/json: schema: type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 /w/{workspace}/jobs_u/completed/get_result/{id}: get: summary: get completed job result @@ -9530,7 +9738,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: suspended_job in: query schema: @@ -9567,10 +9775,10 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: get_started in: query - schema: &ref_124 + schema: &ref_133 type: boolean responses: '200': @@ -9604,7 +9812,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job details @@ -9612,8 +9820,8 @@ paths: application/json: schema: type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 /w/{workspace}/jobs_u/queue/cancel/{id}: post: summary: cancel queued or running job @@ -9628,7 +9836,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: reason required: true @@ -9692,7 +9900,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: reason required: true @@ -9724,7 +9932,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -9755,7 +9963,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -9797,7 +10005,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: payload description: > The base64 encoded payload that has been encoded as a JSON. e.g how @@ -9805,7 +10013,7 @@ paths: `encodeURIComponent(btoa(JSON.stringify({a: 2})))` in: query - schema: *ref_86 + schema: *ref_92 - name: resume_id in: path required: true @@ -9840,7 +10048,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -9882,7 +10090,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: key in: path required: true @@ -9914,7 +10122,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: key in: path required: true @@ -9940,7 +10148,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: required: true content: @@ -9968,7 +10176,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -10003,7 +10211,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -10045,7 +10253,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 - name: resume_id in: path required: true @@ -10069,8 +10277,8 @@ paths: type: object properties: job: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 approvers: type: array items: @@ -10135,7 +10343,7 @@ paths: application/json: schema: type: object - properties: &ref_162 + properties: &ref_169 path: type: string schedule: @@ -10176,7 +10384,7 @@ paths: type: boolean retry: type: object - properties: *ref_87 + properties: *ref_93 no_flow_overlap: type: boolean summary: @@ -10186,7 +10394,7 @@ paths: paused_until: type: string format: date-time - required: &ref_163 + required: &ref_170 - path - schedule - timezone @@ -10222,7 +10430,7 @@ paths: application/json: schema: type: object - properties: &ref_164 + properties: &ref_171 schedule: type: string timezone: @@ -10255,7 +10463,7 @@ paths: type: boolean retry: type: object - properties: *ref_87 + properties: *ref_93 no_flow_overlap: type: boolean summary: @@ -10265,7 +10473,7 @@ paths: paused_until: type: string format: date-time - required: &ref_165 + required: &ref_172 - schedule - timezone - script_path @@ -10356,7 +10564,7 @@ paths: application/json: schema: type: object - properties: &ref_88 + properties: &ref_94 path: type: string edited_by: @@ -10410,7 +10618,7 @@ paths: type: boolean retry: type: object - properties: *ref_87 + properties: *ref_93 summary: type: string no_flow_overlap: @@ -10420,7 +10628,7 @@ paths: paused_until: type: string format: date-time - required: &ref_89 + required: &ref_95 - path - edited_by - edited_at @@ -10477,7 +10685,7 @@ paths: filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: path description: filter by path in: query @@ -10500,8 +10708,8 @@ paths: type: array items: type: object - properties: *ref_88 - required: *ref_89 + properties: *ref_94 + required: *ref_95 /w/{workspace}/schedules/list_with_jobs: get: summary: list schedules with last 20 jobs @@ -10529,10 +10737,10 @@ paths: schema: type: array items: - allOf: &ref_161 + allOf: &ref_168 - type: object - properties: *ref_88 - required: *ref_89 + properties: *ref_94 + required: *ref_95 - type: object properties: jobs: @@ -10611,7 +10819,7 @@ paths: application/json: schema: type: object - properties: &ref_166 + properties: &ref_173 path: type: string script_path: @@ -10632,7 +10840,7 @@ paths: type: boolean requires_auth: type: boolean - required: &ref_167 + required: &ref_174 - path - script_path - route_path @@ -10669,7 +10877,7 @@ paths: application/json: schema: type: object - properties: &ref_168 + properties: &ref_175 path: type: string script_path: @@ -10690,7 +10898,7 @@ paths: type: boolean requires_auth: type: boolean - required: &ref_169 + required: &ref_176 - path - script_path - is_flow @@ -10749,7 +10957,7 @@ paths: application/json: schema: type: object - properties: &ref_90 + properties: &ref_96 path: type: string edited_by: @@ -10783,7 +10991,7 @@ paths: type: boolean requires_auth: type: boolean - required: &ref_91 + required: &ref_97 - path - edited_by - edited_at @@ -10837,8 +11045,8 @@ paths: type: array items: type: object - properties: *ref_90 - required: *ref_91 + properties: *ref_96 + required: *ref_97 /w/{workspace}/http_triggers/exists/{path}: get: summary: does http trigger exists @@ -10901,24 +11109,362 @@ paths: application/json: schema: type: boolean - /w/{workspace}/http_triggers/used: - get: - summary: whether http triggers are used - operationId: used + /w/{workspace}/websocket_triggers/create: + post: + summary: create websocket trigger + operationId: createWebsocketTrigger tags: - - http_trigger + - websocket_trigger parameters: - name: workspace in: path required: true schema: *ref_0 + requestBody: + description: new websocket trigger + required: true + content: + application/json: + schema: + type: object + properties: &ref_177 + path: + type: string + script_path: + type: string + is_flow: + type: boolean + url: + type: string + enabled: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + anyOf: &ref_98 + - type: object + properties: + raw_message: + type: string + required: + - raw_message + - type: object + properties: + runnable_result: + type: object + properties: + path: + type: string + args: + type: object + additionalProperties: *ref_16 + is_flow: + type: boolean + required: + - path + - args + - is_flow + required: + - runnable_result + url_runnable_args: + type: object + additionalProperties: *ref_16 + required: &ref_178 + - path + - script_path + - url + - is_flow + - filters + - initial_messages + - url_runnable_args + responses: + '201': + description: websocket trigger created + content: + text/plain: + schema: + type: string + /w/{workspace}/websocket_triggers/update/{path}: + post: + summary: update websocket trigger + operationId: updateWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + type: object + properties: &ref_179 + url: + type: string + path: + type: string + script_path: + type: string + is_flow: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + anyOf: *ref_98 + url_runnable_args: + type: object + additionalProperties: *ref_16 + required: &ref_180 + - path + - script_path + - url + - is_flow + - filters + - initial_messages + - url_runnable_args responses: '200': - description: whether http triggers are used + description: websocket trigger updated + content: + text/plain: + schema: + type: string + /w/{workspace}/websocket_triggers/delete/{path}: + delete: + summary: delete websocket trigger + operationId: deleteWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + responses: + '200': + description: websocket trigger deleted + content: + text/plain: + schema: + type: string + /w/{workspace}/websocket_triggers/get/{path}: + get: + summary: get websocket trigger + operationId: getWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + responses: + '200': + description: websocket trigger deleted + content: + application/json: + schema: + type: object + properties: &ref_99 + path: + type: string + edited_by: + type: string + edited_at: + type: string + format: date-time + script_path: + type: string + url: + type: string + is_flow: + type: boolean + extra_perms: + type: object + additionalProperties: + type: boolean + email: + type: string + workspace_id: + type: string + server_id: + type: string + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + filters: + type: array + items: + type: object + properties: + key: + type: string + value: {} + required: + - key + - value + initial_messages: + type: array + items: + anyOf: *ref_98 + url_runnable_args: + type: object + additionalProperties: *ref_16 + required: &ref_100 + - path + - edited_by + - edited_at + - script_path + - url + - extra_perms + - is_flow + - email + - workspace_id + - enabled + - filters + - initial_messages + - url_runnable_args + /w/{workspace}/websocket_triggers/list: + get: + summary: list websocket triggers + operationId: listWebsocketTriggers + tags: + - websocket_trigger + parameters: + - required: true + name: workspace + in: path + schema: *ref_0 + - name: page + description: which page to return (start at 1, default 1) + in: query + schema: *ref_5 + - name: per_page + description: number of items to return for a given page (default 30, max 100) + in: query + schema: *ref_6 + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + '200': + description: websocket trigger list + content: + application/json: + schema: + type: array + items: + type: object + properties: *ref_99 + required: *ref_100 + /w/{workspace}/websocket_triggers/exists/{path}: + get: + summary: does websocket trigger exists + operationId: existsWebsocketTrigger + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + responses: + '200': + description: websocket trigger exists content: application/json: schema: type: boolean + /w/{workspace}/websocket_triggers/setenabled/{path}: + post: + summary: set enabled websocket trigger + operationId: setWebsocketTriggerEnabled + tags: + - websocket_trigger + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + - name: path + in: path + required: true + schema: *ref_21 + requestBody: + description: updated websocket trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + '200': + description: websocket trigger enabled set + content: + text/plain: + schema: + type: string /groups/list: get: summary: list instance groups @@ -10934,7 +11480,7 @@ paths: type: array items: type: object - properties: &ref_93 + properties: &ref_102 name: type: string summary: @@ -10943,7 +11489,7 @@ paths: type: array items: type: string - required: &ref_94 + required: &ref_103 - name /groups/get/{name}: get: @@ -10955,7 +11501,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: instance group @@ -10963,8 +11509,8 @@ paths: application/json: schema: type: object - properties: *ref_93 - required: *ref_94 + properties: *ref_102 + required: *ref_103 /groups/create: post: summary: create instance group @@ -11002,7 +11548,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: update instance group required: true @@ -11032,7 +11578,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: instance group deleted @@ -11050,7 +11596,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: user to add to instance group required: true @@ -11080,7 +11626,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: user to remove from instance group required: true @@ -11115,7 +11661,7 @@ paths: type: array items: type: object - properties: &ref_95 + properties: &ref_104 name: type: string summary: @@ -11130,7 +11676,7 @@ paths: type: string external_id: type: string - required: &ref_96 + required: &ref_105 - name /groups/overwrite: post: @@ -11147,8 +11693,8 @@ paths: type: array items: type: object - properties: *ref_95 - required: *ref_96 + properties: *ref_104 + required: *ref_105 responses: '200': description: success message @@ -11184,7 +11730,7 @@ paths: type: array items: type: object - properties: &ref_97 + properties: &ref_106 name: type: string summary: @@ -11197,7 +11743,7 @@ paths: type: object additionalProperties: type: boolean - required: &ref_98 + required: &ref_107 - name /w/{workspace}/groups/listnames: get: @@ -11270,7 +11816,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: updated group required: true @@ -11302,7 +11848,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: group deleted @@ -11324,7 +11870,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: group @@ -11332,8 +11878,8 @@ paths: application/json: schema: type: object - properties: *ref_97 - required: *ref_98 + properties: *ref_106 + required: *ref_107 /w/{workspace}/groups/adduser/{name}: post: summary: add user to group @@ -11348,7 +11894,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: added user to group required: true @@ -11380,7 +11926,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: added user to group required: true @@ -11426,7 +11972,7 @@ paths: type: array items: type: object - properties: &ref_99 + properties: &ref_108 name: type: string owners: @@ -11444,7 +11990,7 @@ paths: edited_at: type: string format: date-time - required: &ref_100 + required: &ref_109 - name - owners - extra_perms @@ -11526,7 +12072,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: update folder required: true @@ -11565,7 +12111,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: folder deleted @@ -11587,7 +12133,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: folder @@ -11595,8 +12141,8 @@ paths: application/json: schema: type: object - properties: *ref_99 - required: *ref_100 + properties: *ref_108 + required: *ref_109 /w/{workspace}/folders/getusage/{name}: get: summary: get folder usage @@ -11611,7 +12157,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: folder @@ -11653,7 +12199,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: owner user to folder required: true @@ -11687,7 +12233,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: added owner to folder required: true @@ -11741,7 +12287,7 @@ paths: type: array items: type: object - properties: &ref_170 + properties: &ref_181 worker: type: string worker_instance: @@ -11783,7 +12329,7 @@ paths: type: number wm_memory_usage: type: number - required: &ref_171 + required: &ref_182 - worker - worker_instance - ping_at @@ -11876,7 +12422,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: a config @@ -11893,7 +12439,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 requestBody: description: worker group required: true @@ -11916,7 +12462,7 @@ paths: - name: name in: path required: true - schema: *ref_92 + schema: *ref_101 responses: '200': description: Delete config @@ -11939,13 +12485,49 @@ paths: type: array items: type: object - properties: &ref_216 + properties: &ref_223 name: type: string config: type: object - required: &ref_217 + required: &ref_224 - name + /configs/list_autoscaling_events/{worker_group}: + get: + summary: List autoscaling events + operationId: listAutoscalingEvents + tags: + - config + parameters: + - name: worker_group + in: path + required: true + schema: + type: string + responses: + '200': + description: List of autoscaling events + content: + application/json: + schema: + type: array + items: + type: object + properties: &ref_227 + id: + type: integer + format: int64 + worker_group: + type: string + event_type: + type: string + desired_workers: + type: integer + reason: + type: string + applied_at: + type: string + format: date-time /w/{workspace}/acls/get/{kind}/{path}: get: summary: get granular acls @@ -11977,6 +12559,7 @@ paths: - app - raw_app - http_trigger + - websocket_trigger responses: '200': description: acls @@ -12017,6 +12600,7 @@ paths: - app - raw_app - http_trigger + - websocket_trigger requestBody: description: acl to add required: true @@ -12069,6 +12653,7 @@ paths: - app - raw_app - http_trigger + - websocket_trigger requestBody: description: acl to add required: true @@ -12217,13 +12802,13 @@ paths: schema: *ref_0 - name: runnable_id in: query - schema: &ref_101 + schema: &ref_110 type: string - name: runnable_type in: query - schema: &ref_102 + schema: &ref_111 type: string - enum: &ref_133 + enum: &ref_140 - ScriptHash - ScriptPath - FlowPath @@ -12244,7 +12829,7 @@ paths: type: array items: type: object - properties: &ref_103 + properties: &ref_112 id: type: string name: @@ -12258,7 +12843,7 @@ paths: type: boolean success: type: boolean - required: &ref_104 + required: &ref_113 - id - name - args @@ -12308,10 +12893,10 @@ paths: schema: *ref_0 - name: runnable_id in: query - schema: *ref_101 + schema: *ref_110 - name: runnable_type in: query - schema: *ref_102 + schema: *ref_111 - name: page description: which page to return (start at 1, default 1) in: query @@ -12329,8 +12914,8 @@ paths: type: array items: type: object - properties: *ref_103 - required: *ref_104 + properties: *ref_112 + required: *ref_113 /w/{workspace}/inputs/create: post: summary: Create an Input for future use in a script or flow @@ -12344,10 +12929,10 @@ paths: schema: *ref_0 - name: runnable_id in: query - schema: *ref_101 + schema: *ref_110 - name: runnable_type in: query - schema: *ref_102 + schema: *ref_111 requestBody: description: Input required: true @@ -12355,12 +12940,12 @@ paths: application/json: schema: type: object - properties: &ref_129 + properties: &ref_136 name: type: string args: type: object - required: &ref_130 + required: &ref_137 - name - args - created_by @@ -12390,14 +12975,14 @@ paths: application/json: schema: type: object - properties: &ref_131 + properties: &ref_138 id: type: string name: type: string is_public: type: boolean - required: &ref_132 + required: &ref_139 - id - name - is_public @@ -12423,7 +13008,7 @@ paths: - name: input in: path required: true - schema: &ref_123 + schema: &ref_132 type: string responses: '200': @@ -12456,7 +13041,7 @@ paths: properties: s3_resource: type: object - properties: &ref_105 + properties: &ref_114 bucket: type: string region: @@ -12471,7 +13056,7 @@ paths: type: string pathStyle: type: boolean - required: &ref_106 + required: &ref_115 - bucket - region - endPoint @@ -12547,8 +13132,8 @@ paths: properties: s3_resource: type: object - properties: *ref_105 - required: *ref_106 + properties: *ref_114 + required: *ref_115 responses: '200': description: Connection settings @@ -12569,10 +13154,10 @@ paths: type: boolean client_kwargs: type: object - properties: &ref_107 + properties: &ref_116 region_name: type: string - required: &ref_108 + required: &ref_117 - region_name required: - endpoint_url @@ -12627,8 +13212,8 @@ paths: type: boolean client_kwargs: type: object - properties: *ref_107 - required: *ref_108 + properties: *ref_116 + required: *ref_117 required: - endpoint_url - use_ssl @@ -12686,8 +13271,8 @@ paths: application/json: schema: type: object - properties: *ref_105 - required: *ref_106 + properties: *ref_114 + required: *ref_115 /w/{workspace}/job_helpers/test_connection: get: summary: Test connection to the workspace object storage @@ -12751,10 +13336,10 @@ paths: type: array items: type: object - properties: &ref_193 + properties: &ref_200 s3: type: string - required: &ref_194 + required: &ref_201 - s3 restricted_access: type: boolean @@ -12787,7 +13372,7 @@ paths: application/json: schema: type: object - properties: &ref_195 + properties: &ref_202 mime_type: type: string size_in_bytes: @@ -12851,7 +13436,7 @@ paths: application/json: schema: type: object - properties: &ref_196 + properties: &ref_203 msg: type: string content: @@ -12863,7 +13448,7 @@ paths: - Csv - Parquet - Unknown - required: &ref_197 + required: &ref_204 - content_type /w/{workspace}/job_helpers/load_parquet_preview/{path}: get: @@ -13201,7 +13786,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: parameters for statistics retrieval required: true @@ -13230,46 +13815,46 @@ paths: type: array items: type: object - properties: &ref_200 + properties: &ref_207 id: type: string name: type: string - required: &ref_201 + required: &ref_208 - id scalar_metrics: type: array items: type: object - properties: &ref_202 + properties: &ref_209 metric_id: type: string value: type: number - required: &ref_203 + required: &ref_210 - id - value timeseries_metrics: type: array items: type: object - properties: &ref_204 + properties: &ref_211 metric_id: type: string values: type: array items: type: object - properties: &ref_206 + properties: &ref_213 timestamp: type: string format: date-time value: type: number - required: &ref_207 + required: &ref_214 - timestamp - value - required: &ref_205 + required: &ref_212 - id - values /w/{workspace}/job_metrics/set_progress/{id}: @@ -13286,7 +13871,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 requestBody: description: parameters for statistics retrieval required: true @@ -13320,7 +13905,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: job progress between 0 and 99 @@ -13338,11 +13923,11 @@ paths: - name: before description: filter on started before (inclusive) timestamp in: query - schema: *ref_109 + schema: *ref_118 - name: after description: filter on created after (exclusive) timestamp in: query - schema: *ref_110 + schema: *ref_119 - name: with_error in: query required: false @@ -13414,12 +13999,12 @@ paths: type: array items: type: object - properties: &ref_210 + properties: &ref_217 concurrency_key: type: string total_running: type: number - required: &ref_211 + required: &ref_218 - concurrency_key - total_running /concurrency_groups/prune/{concurrency_id}: @@ -13432,7 +14017,7 @@ paths: - name: concurrency_id in: path required: true - schema: &ref_125 + schema: &ref_134 type: string responses: '200': @@ -13452,7 +14037,7 @@ paths: - name: id in: path required: true - schema: *ref_83 + schema: *ref_89 responses: '200': description: concurrency key for given job @@ -13485,93 +14070,93 @@ paths: - name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 - name: label description: >- mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: *ref_78 + schema: *ref_84 - name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 - name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 - name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 - name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 - name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 - name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 - name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 - name: created_or_started_before description: >- filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query - schema: *ref_111 + schema: *ref_120 - name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 - name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 - name: created_or_started_after description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query - schema: *ref_112 + schema: *ref_121 - name: created_or_started_after_completed_jobs description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs in: query - schema: *ref_113 + schema: *ref_122 - name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 - name: args description: >- filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 - name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 - name: result description: >- filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 - name: page description: which page to return (start at 1, default 1) in: query @@ -13619,17 +14204,17 @@ paths: application/json: schema: type: object - properties: &ref_212 + properties: &ref_219 jobs: type: array items: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 obscured_jobs: type: array items: type: object - properties: &ref_134 + properties: &ref_141 typ: type: string started_at: @@ -13642,7 +14227,7 @@ paths: Obscured jobs omitted for security because of too specific filtering type: boolean - required: &ref_213 + required: &ref_220 - jobs - obscured_jobs /srch/w/{workspace}/index/search/job: @@ -13684,9 +14269,118 @@ paths: type: array items: type: object - properties: &ref_218 + properties: &ref_225 dancer: type: string + /srch/index/search/service_logs: + get: + summary: Search through service logs with a string query + operationId: searchLogsIndex + tags: + - indexSearch + parameters: + - name: search_query + in: query + required: true + schema: + type: string + - name: mode + in: query + required: true + schema: + type: string + - name: worker_group + in: query + required: false + schema: + type: string + - name: hostname + in: query + required: true + schema: + type: string + - name: min_ts + in: query + required: false + schema: + type: string + format: date-time + - name: max_ts + in: query + required: false + schema: + type: string + format: date-time + responses: + '200': + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: >- + a list of the terms that couldn't be parsed (and thus + ignored) + type: array + items: + type: string + hits: + description: log files that matched the query + type: array + items: + type: object + properties: &ref_226 + dancer: + type: string + /srch/index/search/count_service_logs: + get: + summary: Search and count the log line hits on every provided host + operationId: countSearchLogsIndex + tags: + - indexSearch + parameters: + - name: search_query + in: query + required: true + schema: + type: string + - name: hosts + in: query + required: true + schema: + type: string + - name: min_ts + in: query + required: false + schema: + type: string + format: date-time + - name: max_ts + in: query + required: false + schema: + type: string + format: date-time + responses: + '200': + description: search results + content: + application/json: + schema: + type: object + properties: + query_parse_errors: + description: >- + a list of the terms that couldn't be parsed (and thus + ignored) + type: array + items: + type: string + count_per_host: + description: count of log lines that matched the query per hostname + type: object components: securitySchemes: bearerAuth: @@ -13711,12 +14405,12 @@ components: name: version in: path required: true - schema: *ref_114 + schema: *ref_123 Token: name: token in: path required: true - schema: *ref_115 + schema: *ref_124 AccountId: name: id in: path @@ -13741,7 +14435,7 @@ components: name: id in: path required: true - schema: *ref_83 + schema: *ref_89 Path: name: path in: path @@ -13756,12 +14450,12 @@ components: name: version in: path required: true - schema: *ref_116 + schema: *ref_125 Name: name: name in: path required: true - schema: *ref_92 + schema: *ref_101 Page: name: page description: which page to return (start at 1, default 1) @@ -13776,12 +14470,12 @@ components: name: order_desc description: order by desc order (default true) in: query - schema: *ref_48 + schema: *ref_50 CreatedBy: name: created_by description: mask to filter exact matching user creator in: query - schema: *ref_49 + schema: *ref_51 Label: name: label description: >- @@ -13789,26 +14483,26 @@ components: with as a result an object containing a string in the array at key 'wm_labels') in: query - schema: *ref_78 + schema: *ref_84 ParentJob: name: parent_job description: >- The parent job that is at the origin and responsible for the execution of this script if any in: query - schema: *ref_42 + schema: *ref_44 WorkerTag: name: tag description: Override the tag to use in: query - schema: *ref_44 + schema: *ref_46 CacheTtl: name: cache_ttl description: >- Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl in: query - schema: *ref_45 + schema: *ref_47 NewJobId: name: job_id description: >- @@ -13816,7 +14510,7 @@ components: randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) in: query - schema: *ref_43 + schema: *ref_45 IncludeHeader: name: include_header description: > @@ -13826,14 +14520,14 @@ components: Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key in: query - schema: *ref_46 + schema: *ref_48 QueueLimit: name: queue_limit description: > The maximum size of the queue for which the request would get rejected if that job would push it above that limit in: query - schema: *ref_47 + schema: *ref_49 Payload: name: payload description: > @@ -13842,162 +14536,162 @@ components: `encodeURIComponent(btoa(JSON.stringify({a: 2})))` in: query - schema: *ref_86 + schema: *ref_92 ScriptStartPath: name: script_path_start description: mask to filter matching starting path in: query - schema: *ref_63 + schema: *ref_69 SchedulePath: name: schedule_path description: mask to filter by schedule path in: query - schema: *ref_64 + schema: *ref_70 ScriptExactPath: name: script_path_exact description: mask to filter exact matching path in: query - schema: *ref_62 + schema: *ref_68 ScriptExactHash: name: script_hash description: mask to filter exact matching path in: query - schema: *ref_65 + schema: *ref_71 CreatedBefore: name: created_before description: filter on created before (inclusive) timestamp in: query - schema: *ref_117 + schema: *ref_126 CreatedAfter: name: created_after description: filter on created after (exclusive) timestamp in: query - schema: *ref_118 + schema: *ref_127 StartedBefore: name: started_before description: filter on started before (inclusive) timestamp in: query - schema: *ref_66 + schema: *ref_72 StartedAfter: name: started_after description: filter on started after (exclusive) timestamp in: query - schema: *ref_67 + schema: *ref_73 Before: name: before description: filter on started before (inclusive) timestamp in: query - schema: *ref_109 + schema: *ref_118 CreatedOrStartedAfter: name: created_or_started_after description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query - schema: *ref_112 + schema: *ref_121 CreatedOrStartedAfterCompletedJob: name: created_or_started_after_completed_jobs description: >- filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs in: query - schema: *ref_113 + schema: *ref_122 CreatedOrStartedBefore: name: created_or_started_before description: >- filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query - schema: *ref_111 + schema: *ref_120 Success: name: success description: filter on successful jobs in: query - schema: *ref_68 + schema: *ref_74 ScheduledForBeforeNow: name: scheduled_for_before_now description: filter on jobs scheduled_for before now (hence waitinf for a worker) in: query - schema: *ref_69 + schema: *ref_75 Suspended: name: suspended description: filter on suspended jobs in: query - schema: *ref_71 + schema: *ref_77 Running: name: running description: filter on running jobs in: query - schema: *ref_72 + schema: *ref_78 ArgsFilter: name: args description: filter on jobs containing those args as a json subset (@> in postgres) in: query - schema: *ref_73 + schema: *ref_79 Tag: name: tag description: filter on jobs with a given tag/worker group in: query - schema: *ref_75 + schema: *ref_81 ResultFilter: name: result description: filter on jobs containing those result as a json subset (@> in postgres) in: query - schema: *ref_74 + schema: *ref_80 After: name: after description: filter on created after (exclusive) timestamp in: query - schema: *ref_110 + schema: *ref_119 Username: name: username description: filter on exact username of user in: query - schema: *ref_119 + schema: *ref_128 Operation: name: operation description: filter on exact or prefix name of operation in: query - schema: *ref_120 + schema: *ref_129 ResourceName: name: resource description: filter on exact or prefix name of resource in: query - schema: *ref_121 + schema: *ref_130 ActionKind: name: action_kind description: filter on type of operation in: query - schema: *ref_122 + schema: *ref_131 JobKinds: name: job_kinds description: >- filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query - schema: *ref_70 + schema: *ref_76 RunnableId: name: runnable_id in: query - schema: *ref_101 + schema: *ref_110 RunnableTypeQuery: name: runnable_type in: query - schema: *ref_102 + schema: *ref_111 InputId: name: input in: path required: true - schema: *ref_123 + schema: *ref_132 GetStarted: name: get_started in: query - schema: *ref_124 + schema: *ref_133 ConcurrencyId: name: concurrency_id in: path required: true - schema: *ref_125 + schema: *ref_134 schemas: Script: type: object @@ -14008,83 +14702,83 @@ components: properties: *ref_40 required: *ref_41 NewScriptWithDraft: - allOf: *ref_126 + allOf: *ref_135 ScriptHistory: type: object - properties: *ref_127 - required: *ref_128 + properties: *ref_42 + required: *ref_43 ScriptArgs: type: object additionalProperties: *ref_16 Input: type: object - properties: *ref_103 - required: *ref_104 + properties: *ref_112 + required: *ref_113 CreateInput: type: object - properties: *ref_129 - required: *ref_130 + properties: *ref_136 + required: *ref_137 UpdateInput: type: object - properties: *ref_131 - required: *ref_132 + properties: *ref_138 + required: *ref_139 RunnableType: type: string - enum: *ref_133 + enum: *ref_140 QueuedJob: type: object - properties: *ref_81 - required: *ref_82 + properties: *ref_87 + required: *ref_88 CompletedJob: type: object - properties: *ref_79 - required: *ref_80 + properties: *ref_85 + required: *ref_86 ObscuredJob: type: object - properties: *ref_134 + properties: *ref_141 Job: - oneOf: *ref_84 - discriminator: *ref_85 + oneOf: *ref_90 + discriminator: *ref_91 User: type: object properties: *ref_10 required: *ref_11 UserUsage: type: object - properties: *ref_135 + properties: *ref_142 Login: type: object - properties: *ref_136 - required: *ref_137 + properties: *ref_143 + required: *ref_144 EditWorkspaceUser: type: object - properties: *ref_138 + properties: *ref_145 TruncatedToken: type: object properties: *ref_38 required: *ref_39 NewToken: type: object - properties: *ref_139 + properties: *ref_146 NewTokenImpersonate: type: object - properties: *ref_140 - required: *ref_141 + properties: *ref_147 + required: *ref_148 ListableVariable: type: object properties: *ref_22 required: *ref_23 ContextualVariable: type: object - properties: *ref_142 - required: *ref_143 + properties: *ref_149 + required: *ref_150 CreateVariable: type: object - properties: *ref_144 - required: *ref_145 + properties: *ref_151 + required: *ref_152 EditVariable: type: object - properties: *ref_146 + properties: *ref_153 AuditLog: type: object properties: *ref_1 @@ -14216,93 +14910,107 @@ components: - has_preprocessor Preview: type: object - properties: *ref_147 - required: *ref_148 + properties: *ref_154 + required: *ref_155 WorkflowTask: - type: object - properties: *ref_149 - required: *ref_150 - WorkflowStatusRecord: - type: object - additionalProperties: *ref_151 - WorkflowStatus: - type: object - properties: *ref_152 - CreateResource: - type: object - properties: *ref_153 - required: *ref_154 - EditResource: - type: object - properties: *ref_155 - Resource: type: object properties: *ref_156 required: *ref_157 + WorkflowStatusRecord: + type: object + additionalProperties: *ref_158 + WorkflowStatus: + type: object + properties: *ref_159 + CreateResource: + type: object + properties: *ref_160 + required: *ref_161 + EditResource: + type: object + properties: *ref_162 + Resource: + type: object + properties: *ref_163 + required: *ref_164 ListableResource: type: object - properties: *ref_158 - required: *ref_159 + properties: *ref_165 + required: *ref_166 ResourceType: type: object properties: *ref_26 required: *ref_27 EditResourceType: type: object - properties: *ref_160 + properties: *ref_167 Schedule: type: object - properties: *ref_88 - required: *ref_89 + properties: *ref_94 + required: *ref_95 ScheduleWJobs: - allOf: *ref_161 + allOf: *ref_168 NewSchedule: type: object - properties: *ref_162 - required: *ref_163 + properties: *ref_169 + required: *ref_170 EditSchedule: type: object - properties: *ref_164 - required: *ref_165 + properties: *ref_171 + required: *ref_172 HttpTrigger: type: object - properties: *ref_90 - required: *ref_91 + properties: *ref_96 + required: *ref_97 NewHttpTrigger: type: object - properties: *ref_166 - required: *ref_167 + properties: *ref_173 + required: *ref_174 EditHttpTrigger: type: object - properties: *ref_168 - required: *ref_169 + properties: *ref_175 + required: *ref_176 TriggersCount: type: object - properties: *ref_53 - Group: - type: object - properties: *ref_97 - required: *ref_98 - InstanceGroup: - type: object - properties: *ref_93 - required: *ref_94 - Folder: + properties: *ref_57 + WebsocketTrigger: type: object properties: *ref_99 required: *ref_100 + NewWebsocketTrigger: + type: object + properties: *ref_177 + required: *ref_178 + EditWebsocketTrigger: + type: object + properties: *ref_179 + required: *ref_180 + WebsocketTriggerInitialMessage: + anyOf: *ref_98 + Group: + type: object + properties: *ref_106 + required: *ref_107 + InstanceGroup: + type: object + properties: *ref_102 + required: *ref_103 + Folder: + type: object + properties: *ref_108 + required: *ref_109 WorkerPing: type: object - properties: *ref_170 - required: *ref_171 + properties: *ref_181 + required: *ref_182 UserWorkspaceList: type: object - properties: *ref_172 - required: *ref_173 + properties: *ref_183 + required: *ref_184 CreateWorkspace: type: object - properties: *ref_174 - required: *ref_175 + properties: *ref_185 + required: *ref_186 Workspace: type: object properties: *ref_7 @@ -14316,48 +15024,48 @@ components: properties: *ref_12 required: *ref_13 Flow: - allOf: *ref_52 + allOf: *ref_56 ExtraPerms: type: object - additionalProperties: *ref_176 + additionalProperties: *ref_187 FlowMetadata: type: object - properties: *ref_177 - required: *ref_178 + properties: *ref_188 + required: *ref_189 OpenFlowWPath: - allOf: *ref_54 + allOf: *ref_58 FlowPreview: type: object - properties: *ref_179 - required: *ref_180 + properties: *ref_190 + required: *ref_191 RestartedFrom: type: object - properties: *ref_181 + properties: *ref_192 Policy: type: object - properties: *ref_55 + properties: *ref_59 ListableApp: type: object - properties: *ref_182 - required: *ref_183 + properties: *ref_193 + required: *ref_194 ListableRawApp: type: object - properties: *ref_184 - required: *ref_185 + properties: *ref_195 + required: *ref_196 AppWithLastVersion: type: object - properties: *ref_56 - required: *ref_57 + properties: *ref_60 + required: *ref_61 AppWithLastVersionWDraft: - allOf: *ref_186 + allOf: *ref_197 AppHistory: type: object - properties: *ref_187 - required: *ref_188 + properties: *ref_62 + required: *ref_63 FlowVersion: type: object - properties: *ref_189 - required: *ref_190 + properties: *ref_54 + required: *ref_55 SlackToken: type: object properties: @@ -14379,33 +15087,33 @@ components: - bot TokenResponse: type: object - properties: *ref_191 - required: *ref_192 + properties: *ref_198 + required: *ref_199 HubScriptKind: name: kind schema: *ref_34 PolarsClientKwargs: type: object - properties: *ref_107 - required: *ref_108 + properties: *ref_116 + required: *ref_117 LargeFileStorage: type: object properties: *ref_17 WindmillLargeFile: type: object - properties: *ref_193 - required: *ref_194 + properties: *ref_200 + required: *ref_201 WindmillFileMetadata: type: object - properties: *ref_195 + properties: *ref_202 WindmillFilePreview: type: object - properties: *ref_196 - required: *ref_197 + properties: *ref_203 + required: *ref_204 S3Resource: type: object - properties: *ref_105 - required: *ref_106 + properties: *ref_114 + required: *ref_115 WorkspaceGitSyncSettings: type: object properties: *ref_18 @@ -14417,8 +15125,8 @@ components: properties: *ref_20 GitRepositorySettings: type: object - properties: *ref_198 - required: *ref_199 + properties: *ref_205 + required: *ref_206 UploadFilePart: type: object properties: @@ -14431,117 +15139,126 @@ components: - tag MetricMetadata: type: object - properties: *ref_200 - required: *ref_201 + properties: *ref_207 + required: *ref_208 ScalarMetric: type: object - properties: *ref_202 - required: *ref_203 + properties: *ref_209 + required: *ref_210 TimeseriesMetric: type: object - properties: *ref_204 - required: *ref_205 + properties: *ref_211 + required: *ref_212 MetricDataPoint: type: object - properties: *ref_206 - required: *ref_207 + properties: *ref_213 + required: *ref_214 RawScriptForDependencies: type: object - properties: *ref_208 - required: *ref_209 + properties: *ref_215 + required: *ref_216 ConcurrencyGroup: type: object - properties: *ref_210 - required: *ref_211 + properties: *ref_217 + required: *ref_218 ExtendedJobs: type: object - properties: *ref_212 - required: *ref_213 + properties: *ref_219 + required: *ref_220 ExportedUser: type: object properties: *ref_3 required: *ref_4 GlobalSetting: - type: object - properties: *ref_214 - required: *ref_215 - Config: - type: object - properties: *ref_216 - required: *ref_217 - ExportedInstanceGroup: - type: object - properties: *ref_95 - required: *ref_96 - JobSearchHit: - type: object - properties: *ref_218 - StaticTransform: - type: object - properties: *ref_219 - required: *ref_220 - JavascriptTransform: type: object properties: *ref_221 required: *ref_222 + Config: + type: object + properties: *ref_223 + required: *ref_224 + ExportedInstanceGroup: + type: object + properties: *ref_104 + required: *ref_105 + JobSearchHit: + type: object + properties: *ref_225 + LogSearchHit: + type: object + properties: *ref_226 + AutoscalingEvent: + type: object + properties: *ref_227 + CriticalAlert: + type: object + properties: *ref_228 + StaticTransform: + type: object + properties: *ref_229 + required: *ref_230 + JavascriptTransform: + type: object + properties: *ref_231 + required: *ref_232 InputTransform: oneOf: *ref_29 discriminator: *ref_30 RawScript: type: object - properties: *ref_223 - required: *ref_224 + properties: *ref_233 + required: *ref_234 PathScript: type: object - properties: *ref_225 - required: *ref_226 + properties: *ref_235 + required: *ref_236 PathFlow: type: object - properties: *ref_227 - required: *ref_228 + properties: *ref_237 + required: *ref_238 FlowModule: type: object properties: *ref_31 required: *ref_32 ForloopFlow: type: object - properties: *ref_229 - required: *ref_230 + properties: *ref_239 + required: *ref_240 WhileloopFlow: type: object - properties: *ref_231 - required: *ref_232 + properties: *ref_241 + required: *ref_242 BranchOne: type: object - properties: *ref_233 - required: *ref_234 + properties: *ref_243 + required: *ref_244 BranchAll: type: object - properties: *ref_235 - required: *ref_236 + properties: *ref_245 + required: *ref_246 Identity: type: object - properties: *ref_237 - required: *ref_238 + properties: *ref_247 + required: *ref_248 FlowModuleValue: - oneOf: *ref_239 - discriminator: *ref_240 + oneOf: *ref_249 + discriminator: *ref_250 Retry: type: object - properties: *ref_87 + properties: *ref_93 FlowValue: type: object - properties: *ref_58 - required: *ref_59 + properties: *ref_64 + required: *ref_65 OpenFlow: type: object - properties: *ref_50 - required: *ref_51 + properties: *ref_52 + required: *ref_53 FlowStatusModule: type: object - properties: *ref_60 - required: *ref_61 + properties: *ref_66 + required: *ref_67 FlowStatus: type: object - properties: *ref_76 - required: *ref_77 + properties: *ref_82 + required: *ref_83 diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 5b274610bd..a85619dde7 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.421.2 + version: 1.423.2 title: Windmill API contact: @@ -190,7 +190,7 @@ paths: schema: type: string - /w/{workspace}/users/{username}: + /w/{workspace}/users/get/{username}: get: summary: get user (require admin privilege) operationId: getUser @@ -773,6 +773,78 @@ paths: schema: type: string + /settings/critical_alerts: + get: + summary: Get all critical alerts + operationId: getCriticalAlerts + tags: + - setting + parameters: + - in: query + name: page + schema: + type: integer + default: 1 + description: The page number to retrieve (minimum value is 1) + - in: query + name: page_size + schema: + type: integer + default: 10 + maximum: 100 + description: Number of alerts per page (maximum is 100) + - in: query + name: acknowledged + schema: + type: boolean + nullable: true + description: Filter by acknowledgment status; true for acknowledged, false for unacknowledged, and omit for all alerts + responses: + "200": + description: Successfully retrieved all critical alerts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + + /settings/critical_alerts/{id}/acknowledge: + post: + summary: Acknowledge a critical alert + operationId: acknowledgeCriticalAlert + tags: + - setting + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The ID of the critical alert to acknowledge + responses: + "200": + description: Successfully acknowledged the critical alert + content: + application/json: + schema: + type: string + example: "Critical alert acknowledged" + + /settings/critical_alerts/acknowledge_all: + post: + summary: Acknowledge all unacknowledged critical alerts + operationId: acknowledgeAllCriticalAlerts + tags: + - setting + responses: + "200": + description: Successfully acknowledged all unacknowledged critical alerts. + content: + application/json: + schema: + type: string + example: "All unacknowledged critical alerts acknowledged" /settings/test_license_key: post: @@ -12114,6 +12186,10 @@ components: type: object additionalProperties: type: object + s3_inputs: + type: array + items: + type: object execution_mode: type: string enum: [viewer, publisher, anonymous] @@ -12721,3 +12797,23 @@ components: type: string format: date-time + CriticalAlert: + type: object + properties: + id: + type: integer + description: Unique identifier for the alert + alert_type: + type: string + description: Type of alert (e.g., critical_error) + message: + type: string + description: The message content of the alert + created_at: + type: string + format: date-time + description: Time when the alert was created + acknowledged: + type: boolean + nullable: true + description: Acknowledgment status of the alert, can be true, false, or null if not set diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index cd65136a2a..4ec07393dc 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -7,6 +7,12 @@ use std::collections::HashMap; * Please see the included NOTICE for copyright information and * LICENSE-AGPL for a copy of the license. */ + +#[cfg(feature = "parquet")] +use crate::{job_helpers_ee::{ + get_random_file_name, get_s3_resource, get_workspace_s3_resource, upload_file_internal, + UploadFileResponse, +}, users::fetch_api_authed_from_permissioned_as}; use crate::{ db::{ApiAuthed, DB}, resources::get_resource_value_interpolated_internal, @@ -23,7 +29,13 @@ use axum::{ Router, }; use hyper::StatusCode; +#[cfg(feature = "parquet")] +use itertools::Itertools; use magic_crypt::MagicCryptTrait; +#[cfg(feature = "parquet")] +use object_store::{Attribute, Attributes}; +#[cfg(feature = "parquet")] +use regex::Regex; use serde::{Deserialize, Serialize}; use serde_json::{json, value::RawValue}; use sha2::{Digest, Sha256}; @@ -32,6 +44,8 @@ use sqlx::{types::Uuid, FromRow}; use std::str; use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; +#[cfg(feature = "parquet")] +use windmill_common::s3_helpers::build_object_store_client; use windmill_common::{ apps::ListAppQuery, db::UserDB, @@ -69,6 +83,7 @@ pub fn workspaced_service() -> Router { pub fn unauthed_service() -> Router { Router::new() .route("/execute_component/*path", post(execute_component)) + .route("/upload_s3_file/*path", post(upload_s3_file_from_app)) .route("/public_app/:secret", get(get_public_app_by_secret)) .route("/public_resource/*path", get(get_public_resource)) } @@ -179,6 +194,14 @@ pub struct PolicyTriggerableInputs { allow_user_resources: AllowUserResources, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct S3Input { + allowed_resources: Vec, + allow_user_resources: bool, + allow_workspace_resource: bool, + file_key_regex: String, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Policy { pub on_behalf_of: Option, @@ -192,6 +215,7 @@ pub struct Policy { #[serde(skip_serializing_if = "Option::is_none")] pub triggerables_v2: Option>, pub execution_mode: ExecutionMode, + pub s3_inputs: Option>, } #[derive(Deserialize)] @@ -432,9 +456,7 @@ async fn get_latest_version( authed: ApiAuthed, Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, - ) -> JsonResult> { - let mut tx = user_db.begin(&authed).await?; let row = sqlx::query!( "SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg @@ -457,7 +479,6 @@ async fn get_latest_version( } else { return Ok(Json(None)); } - } async fn update_app_history( @@ -1067,6 +1088,49 @@ fn digest(code: &str) -> String { format!("rawscript/{:x}", result) } +async fn get_on_behalf_details_from_policy_and_authed( + policy: &Policy, + opt_authed: &Option, +) -> Result<(String, String, String)> { + let (username, permissioned_as, email) = match policy.execution_mode { + ExecutionMode::Anonymous => { + let username = opt_authed + .as_ref() + .map(|a| a.username.clone()) + .unwrap_or_else(|| "anonymous".to_string()); + let (permissioned_as, email) = get_on_behalf_of(&policy)?; + (username, permissioned_as, email) + } + ExecutionMode::Publisher => { + let username = opt_authed + .as_ref() + .map(|a| a.username.clone()) + .ok_or_else(|| { + Error::BadRequest( + "publisher execution mode requires authentication".to_string(), + ) + })?; + let (permissioned_as, email) = get_on_behalf_of(&policy)?; + (username, permissioned_as, email) + } + ExecutionMode::Viewer => { + let (username, email) = opt_authed + .as_ref() + .map(|a| (a.username.clone(), a.email.clone())) + .ok_or_else(|| { + Error::BadRequest("Required to be authed in viewer mode".to_string()) + })?; + ( + username.clone(), + username_to_permissioned_as(&username), + email, + ) + } + }; + + Ok((username, permissioned_as, email)) +} + async fn execute_component( OptAuthed(opt_authed): OptAuthed, Extension(db): Extension, @@ -1129,6 +1193,7 @@ async fn execute_component( triggerables_v2: Some(hm), on_behalf_of: None, on_behalf_of_email: None, + s3_inputs: None, } } _ => { @@ -1146,41 +1211,8 @@ async fn execute_component( } }; - let (username, permissioned_as, email) = match policy.execution_mode { - ExecutionMode::Anonymous => { - let username = opt_authed - .as_ref() - .map(|a| a.username.clone()) - .unwrap_or_else(|| "anonymous".to_string()); - let (permissioned_as, email) = get_on_behalf_of(&policy)?; - (username, permissioned_as, email) - } - ExecutionMode::Publisher => { - let username = opt_authed - .as_ref() - .map(|a| a.username.clone()) - .ok_or_else(|| { - Error::BadRequest( - "publisher execution mode requires authentication".to_string(), - ) - })?; - let (permissioned_as, email) = get_on_behalf_of(&policy)?; - (username, permissioned_as, email) - } - ExecutionMode::Viewer => { - let (username, email) = opt_authed - .as_ref() - .map(|a| (a.username.clone(), a.email.clone())) - .ok_or_else(|| { - Error::BadRequest("Required to be authed in viewer mode".to_string()) - })?; - ( - username.clone(), - username_to_permissioned_as(&username), - email, - ) - } - }; + let (username, permissioned_as, email) = + get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; let (job_payload, (args, job_id), tag) = match payload { ExecuteApp { args, component, raw_code: Some(raw_code), path: None, .. } => { @@ -1249,6 +1281,248 @@ async fn execute_component( Ok(uuid.to_string()) } +#[cfg(not(feature = "parquet"))] +async fn upload_s3_file_from_app() -> Result<()> { + return Err(Error::BadRequest( + "This endpoint requires the parquet feature to be enabled".to_string(), + )); +} + + +#[cfg(feature = "parquet")] +#[derive(Debug, Deserialize, Clone)] +struct UploadFileToS3Query { + file_key: Option, + file_extension: Option, + s3_resource_path: Option, + content_type: Option, + content_disposition: Option, + force_viewer_file_key_regex: Option, + force_viewer_allow_user_resources: Option, + force_viewer_allow_workspace_resource: Option, + force_viewer_allowed_resources: Option, +} + +#[cfg(feature = "parquet")] +async fn upload_s3_file_from_app( + OptAuthed(opt_authed): OptAuthed, + Extension(db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Query(query): Query, + request: axum::extract::Request, +) -> JsonResult { + let policy = if let Some(file_key_regex) = query.force_viewer_file_key_regex { + Some(Policy { + execution_mode: ExecutionMode::Viewer, + triggerables: None, + triggerables_v2: None, + on_behalf_of: None, + on_behalf_of_email: None, + s3_inputs: Some(vec![S3Input { + file_key_regex: file_key_regex, + allow_user_resources: query.force_viewer_allow_user_resources.unwrap_or(false), + allow_workspace_resource: query + .force_viewer_allow_workspace_resource + .unwrap_or(false), + allowed_resources: query + .force_viewer_allowed_resources + .map(|s| s.split(',').map(|s| s.to_string()).collect()) + .unwrap_or_default(), + }]), + }) + } else { + let policy_o = sqlx::query_scalar!( + "SELECT policy from app WHERE path = $1 AND workspace_id = $2", + &path.0, + &w_id + ) + .fetch_optional(&db) + .await?; + + policy_o + .map(|p| serde_json::from_value::(p).map_err(to_anyhow)) + .transpose()? + }; + + let user_db = UserDB::new(db.clone()); + + let (s3_resource_opt, file_key) = if policy.as_ref().is_some_and(|p| p.s3_inputs.is_some()) { + let policy = policy.unwrap(); + let s3_inputs = policy.s3_inputs.as_ref().unwrap(); + + let (username, permissioned_as, email) = + get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; + + let on_behalf_authed = + fetch_api_authed_from_permissioned_as(permissioned_as, email, &w_id, &db, username) + .await?; + + if let Some(file_key) = query.file_key { + // file key is provided => requires workspace, user or list policy and must match the regex + let matching_s3_inputs = if let Some(ref s3_resource_path) = query.s3_resource_path { + s3_inputs + .iter() + .filter(|s3_input| { + s3_input.allowed_resources.contains(s3_resource_path) + || s3_input.allow_user_resources + }) + .sorted_by_key(|i| i.allow_user_resources) // consider user resources last + .collect::>() + } else { + s3_inputs + .iter() + .filter(|s3_input| s3_input.allow_workspace_resource) + .collect::>() + }; + + let matched_input = matching_s3_inputs.iter().find(|s3_input| { + match Regex::new(&s3_input.file_key_regex) { + Ok(re) => re.is_match(&file_key), + Err(e) => { + tracing::error!("Error compiling regex: {}", e); + false + } + } + }); + + if let Some(matched_input) = matched_input { + if let Some(ref s3_resource_path) = query.s3_resource_path { + if matched_input.allow_user_resources { + if let Some(authed) = opt_authed { + ( + Some( + get_s3_resource( + &authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path, + None, + None, + ) + .await?, + ), + file_key, + ) + } else { + return Err(Error::BadRequest( + "User resources are not allowed without being logged in" + .to_string(), + )); + } + } else { + ( + Some( + get_s3_resource( + &on_behalf_authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path, + None, + None, + ) + .await?, + ), + file_key, + ) + } + } else { + let (_, s3_resource_opt) = + get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None) + .await?; + (s3_resource_opt, file_key) + } + } else { + return Err(Error::BadRequest( + "No matching s3 resource found for the given file key".to_string(), + )); + } + } else { + // no file key => requires unnamed upload policy => allow workspace resource and file_key_regex is empty + let has_unnamed_policy = s3_inputs.iter().any(|s3_input| { + s3_input.allow_workspace_resource && s3_input.file_key_regex.is_empty() + }); + + if !has_unnamed_policy { + return Err(Error::BadRequest( + "no policy found for unnamed s3 file uplooad".to_string(), + )); + } + + // for now, we place all files into `windmill_uploads` folder with a random name + // TODO: make the folder configurable via the workspace settings + let file_key = get_random_file_name(query.file_extension); + + let (_, s3_resource_opt) = + get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?; + + (s3_resource_opt, file_key) + } + } else { + // backward compatibility (no policy) + // if no policy but logged in, use the user's auth to get the s3 resource + if let Some(authed) = opt_authed { + let file_key = query + .file_key + .unwrap_or_else(|| get_random_file_name(query.file_extension)); + + if let Some(ref s3_resource_path) = query.s3_resource_path { + ( + Some( + get_s3_resource( + &authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path, + None, + None, + ) + .await?, + ), + file_key, + ) + } else { + let (_, s3_resource) = + get_workspace_s3_resource(&authed, &db, None, "", &w_id, None).await?; + + (s3_resource, file_key) + } + } else { + return Err(Error::BadRequest("Missing s3 policy".to_string())); + } + }; + + let s3_resource = s3_resource_opt.ok_or(Error::InternalErr( + "No files storage resource defined at the workspace level".to_string(), + ))?; + let s3_client = build_object_store_client(&s3_resource).await?; + + let options = Attributes::from_iter(vec![ + ( + Attribute::ContentType, + query.content_type.unwrap_or_else(|| { + mime_guess::from_path(&file_key) + .first_or_octet_stream() + .to_string() + }), + ), + ( + Attribute::ContentDisposition, + query.content_disposition.unwrap_or("inline".to_string()), + ), + ]) + .into(); + + upload_file_internal(s3_client, &file_key, request, options).await?; + + return Ok(Json(UploadFileResponse { file_key })); +} + fn get_on_behalf_of(policy: &Policy) -> Result<(String, String)> { let permissioned_as = policy .on_behalf_of diff --git a/backend/windmill-api/src/job_helpers_ee.rs b/backend/windmill-api/src/job_helpers_ee.rs index b64ccde92d..3033a2ec5a 100644 --- a/backend/windmill-api/src/job_helpers_ee.rs +++ b/backend/windmill-api/src/job_helpers_ee.rs @@ -1,9 +1,21 @@ use axum::Router; +use serde::Serialize; +use uuid::Uuid; +use windmill_common::s3_helpers::StorageResourceType; #[cfg(feature = "parquet")] use crate::db::{ApiAuthed, DB}; #[cfg(feature = "parquet")] +use object_store::{ObjectStore, PutMultipartOpts}; +#[cfg(feature = "parquet")] +use std::sync::Arc; +use windmill_common::error; +#[cfg(feature = "parquet")] use windmill_common::{db::UserDB, s3_helpers::ObjectStoreResource}; +#[derive(Serialize)] +pub struct UploadFileResponse { + pub file_key: String, +} pub fn workspaced_service() -> Router { Router::new() @@ -21,3 +33,29 @@ pub async fn get_workspace_s3_resource<'c>( // implementation is not open source Ok((None, None)) } + +pub fn get_random_file_name(_file_extension: Option) -> String { + todo!() +} + +pub async fn get_s3_resource<'c>( + _authed: &ApiAuthed, + _db: &DB, + _user_db: Option, + _token: &str, + _w_id: &str, + _resource_path: &str, + _resource_type: Option, + _job_id: Option, +) -> error::Result { + todo!() +} + +pub async fn upload_file_internal( + _s3_client: Arc, + _file_key: &str, + _request: axum::extract::Request, + _options: PutMultipartOpts, +) -> error::Result<()> { + todo!() +} diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 7244b2be17..433c859619 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -65,7 +65,7 @@ use windmill_common::{ db::UserDB, error::{self, to_anyhow, Error}, flow_status::{Approval, FlowStatus, FlowStatusModule}, - flows::FlowValue, + flows::{add_virtual_items_if_necessary, FlowValue}, jobs::{script_path_to_payload, CompletedJob, JobKind, JobPayload, QueuedJob, RawCode}, oauth2::HmacSha256, scripts::{ScriptHash, ScriptLang}, @@ -4275,18 +4275,26 @@ async fn run_flow_dependencies_job( wait_result } +#[derive(Deserialize)] +struct BatchRawScript { + content: String, + language: Option, + lock: Option, +} + #[derive(Deserialize)] struct BatchInfo { kind: String, flow_value: Option, path: Option, + rawscript: Option, } #[tracing::instrument(level = "trace", skip_all)] async fn add_batch_jobs( authed: ApiAuthed, Extension(db): Extension, - Extension(rsmq): Extension>, + Extension(_rsmq): Extension>, Path((w_id, n)): Path<(String, i32)>, Json(batch_info): Json, ) -> error::JsonResult> { @@ -4302,6 +4310,10 @@ async fn add_batch_jobs( concurrent_limit, concurrent_time_window_s, timeout, + raw_code, + raw_lock, + raw_flow, + flow_status ) = match batch_info.kind.as_str() { "script" => { if let Some(path) = batch_info.path { @@ -4329,6 +4341,10 @@ async fn add_batch_jobs( concurrent_limit, concurrency_time_window_s, timeout, + None, + None, + None, + None, ) } else { Err(anyhow::anyhow!( @@ -4336,61 +4352,72 @@ async fn add_batch_jobs( ))? } } - "flow" => { - let mut uuids: Vec = Vec::new(); - let payload = if let Some(ref fv) = batch_info.flow_value { - JobPayload::RawFlow { value: fv.clone(), path: None, restarted_from: None } - } else { - if let Some(path) = batch_info.path.as_ref() { - JobPayload::Flow { - path: path.to_string(), - dedicated_worker: None, - apply_preprocessor: false, - } - } else { - Err(anyhow::anyhow!( - "Path is required if no value is not provided" - ))? - } - }; - let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq); - for _ in 0..n { - let ehm = HashMap::new(); - let (uuid, ntx) = push( - &db, - tx, - &w_id, - payload.clone(), - PushArgs::from(&ehm), - authed.display_username(), - &authed.email, - username_to_permissioned_as(&authed.username), + "rawscript" => { + if let Some(rawscript) = batch_info.rawscript { + ( + None, + None, + JobKind::Preview, + rawscript.language, None, None, None, None, None, - false, - false, - None, - true, + Some(rawscript.content), + rawscript.lock, None, None, - None, - None, - Some(&authed.clone().into()), ) - .await?; - tx = PushIsolationLevel::Transaction(ntx); - uuids.push(uuid); + } else { + Err(anyhow::anyhow!( + "rawscript is required for `rawscript` kind" + ))? } - match tx { - PushIsolationLevel::Transaction(tx) => { - tx.commit().await?; - } - _ => (), - } - return Ok(Json(uuids)); + } + "flow" => { + let (mut value, job_kind, path) = if let Some(value) = batch_info.flow_value { + (value, JobKind::FlowPreview, None) + } else if let Some(path) = batch_info.path { + let value_json = sqlx::query!( + "SELECT flow_version.value AS \"value: sqlx::types::Json>\" FROM flow + LEFT JOIN flow_version + ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] + WHERE flow.path = $1 AND flow.workspace_id = $2", + &path, &w_id + ) + .fetch_optional(&db) + .await? + .ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", path)))?; + let value = + serde_json::from_str::(value_json.value.get()).map_err(|err| { + Error::InternalErr(format!( + "could not convert json to flow for {path}: {err:?}" + )) + })?; + (value, JobKind::Flow, Some(path)) + } else { + Err(anyhow::anyhow!( + "Path is required if no value is not provided" + ))? + }; + add_virtual_items_if_necessary(&mut value.modules); + let flow_status = FlowStatus::new(&value); + ( + None, // script_hash + path, // script_path + job_kind, // job_kind + None, // language + None, // dedicated_worker + value.concurrency_key.clone(), // custom_concurrency_key + value.concurrent_limit.clone(), // concurrent_limit + value.concurrency_time_window_s, // concurrency_time_window_s + None, // timeout + None, // raw_code + None, // raw_lock + Some(value), // raw_flow + Some(flow_status), // flow_status + ) } "noop" => ( None, @@ -4402,6 +4429,10 @@ async fn add_batch_jobs( None, None, None, + None, + None, + None, + None, ), _ => { return Err(error::Error::BadRequest(format!( @@ -4428,8 +4459,8 @@ async fn add_batch_jobs( select gen_random_uuid() as uuid from generate_series(1, $11) ) INSERT INTO queue - (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout) - (SELECT uuid, $1, $2, $3, $4, ('{ "uuid": "' || uuid || '" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14 FROM uuid_table) + (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, raw_code, raw_lock, raw_flow, flow_status) + (SELECT uuid, $1, $2, $3, $4, ('{ "uuid": "' || uuid || '" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15, $16, $17, $18 FROM uuid_table) RETURNING id"#, hash.map(|h| h.0), path, @@ -4444,7 +4475,11 @@ async fn add_batch_jobs( n, concurrent_limit, concurrent_time_window_s, - timeout + timeout, + raw_code, + raw_lock, + raw_flow.map(sqlx::types::Json) as Option>, + flow_status.map(sqlx::types::Json) as Option> ) .fetch_all(&db) .await?; diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index 0299145a38..5dc46e4382 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -58,7 +58,10 @@ pub fn global_service() -> Router { ) .route("/renew_license_key", post(renew_license_key)) .route("/customer_portal", post(create_customer_portal_session)) - .route("/test_critical_channels", post(test_critical_channels)); + .route("/test_critical_channels", post(test_critical_channels)) + .route("/critical_alerts", get(get_critical_alerts)) + .route("/critical_alerts/:id/acknowledge", post(acknowledge_critical_alert)) + .route("/critical_alerts/acknowledge_all", post(acknowledge_all_critical_alerts)); #[cfg(feature = "parquet")] { @@ -430,3 +433,117 @@ pub async fn test_critical_channels( pub async fn test_critical_channels() -> Result { Ok("Critical channels require EE".to_string()) } + +use serde::Serialize; + +#[derive(Serialize)] +pub struct CriticalAlert { + id: i32, + alert_type: String, + message: String, + created_at: chrono::DateTime, + acknowledged: Option, +} + +#[cfg(feature = "enterprise")] +#[derive(Deserialize)] +pub struct AlertQueryParams { + pub page: Option, + pub page_size: Option, + pub acknowledged: Option, +} + +#[cfg(feature = "enterprise")] +pub async fn get_critical_alerts( + Extension(db): Extension, + authed: ApiAuthed, + Query(params): Query, +) -> JsonResult> { + require_super_admin(&db, &authed.email).await?; + + // Default pagination values if not provided + let page = params.page.unwrap_or(1).max(1); + let page_size = params.page_size.unwrap_or(10).min(100) as i64; + let offset = ((page - 1) * page_size as i32) as i64; + + let alerts = if let Some(acknowledged) = params.acknowledged { + sqlx::query_as!( + CriticalAlert, + "SELECT id, alert_type, message, created_at, acknowledged + FROM alerts + WHERE acknowledged = $1 + ORDER BY created_at DESC + LIMIT $2 OFFSET $3", + acknowledged, + page_size, + offset + ) + .fetch_all(&db) + .await? + } else { + sqlx::query_as!( + CriticalAlert, + "SELECT id, alert_type, message, created_at, acknowledged + FROM alerts + ORDER BY created_at DESC + LIMIT $1 OFFSET $2", + page_size, + offset + ) + .fetch_all(&db) + .await? + }; + + Ok(Json(alerts)) +} + +#[cfg(not(feature = "enterprise"))] +pub async fn get_critical_alerts() -> error::Error { + error::Error::NotFound("Critical Alerts require EE".to_string()) +} + +#[cfg(feature = "enterprise")] +pub async fn acknowledge_critical_alert( + Extension(db): Extension, + authed: ApiAuthed, + Path(id): Path, +) -> error::Result { + require_super_admin(&db, &authed.email).await?; + + sqlx::query!( + "UPDATE alerts SET acknowledged = true WHERE id = $1", + id + ) + .execute(&db) + .await?; + + tracing::info!("Acknowledged critical alert with id: {}", id); + Ok("Critical alert acknowledged".to_string()) +} + +#[cfg(not(feature = "enterprise"))] +pub async fn acknowledge_critical_alert() -> error::Error { + error::Error::NotFound("Critical Alerts require EE".to_string()) +} + +#[cfg(feature = "enterprise")] +pub async fn acknowledge_all_critical_alerts( + Extension(db): Extension, + authed: ApiAuthed, +) -> error::Result { + require_super_admin(&db, &authed.email).await?; + + sqlx::query!( + "UPDATE alerts SET acknowledged = true WHERE acknowledged = false" + ) + .execute(&db) + .await?; + + tracing::info!("Acknowledged all unacknowledged critical alerts"); + Ok("All unacknowledged critical alerts acknowledged".to_string()) +} + +#[cfg(not(feature = "enterprise"))] +pub async fn acknowledge_all_critical_alerts() -> error::Error { + error::Error::NotFound("Critical Alerts require EE".to_string()) +} diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 005dfad342..380084d524 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -746,10 +746,20 @@ pub async fn fetch_api_authed( username_override: String, ) -> error::Result { let permissioned_as = username_to_permissioned_as(username.as_str()); + fetch_api_authed_from_permissioned_as(permissioned_as, email, w_id, db, username_override).await +} + +pub async fn fetch_api_authed_from_permissioned_as( + permissioned_as: String, + email: String, + w_id: &str, + db: &DB, + username_override: String, +) -> error::Result { let authed = fetch_authed_from_permissioned_as(permissioned_as, email.clone(), w_id, db).await?; Ok(ApiAuthed { - username: username, + username: authed.username, email: email, is_admin: authed.is_admin, is_operator: authed.is_operator, diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index d357a5ec72..5d7e1b4621 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -16,6 +16,7 @@ use rand::Rng; use serde::{Deserialize, Serialize, Serializer}; use crate::{ + error::Error, more_serde::{default_empty_string, default_id, default_null, default_true, is_default}, scripts::{Schema, ScriptHash, ScriptLang}, }; @@ -651,3 +652,29 @@ pub fn add_virtual_items_if_necessary(modules: &mut Vec) { }); } } + +pub async fn has_failure_module<'c>(flow: sqlx::types::Uuid, db: &sqlx::Pool, completed: bool) -> Result { + if completed { + sqlx::query_scalar!( + "SELECT raw_flow->'failure_module' != 'null'::jsonb + FROM completed_job + WHERE id = $1", + flow + ) + } else { + sqlx::query_scalar!( + "SELECT raw_flow->'failure_module' != 'null'::jsonb + FROM queue + WHERE id = $1", + flow + ) + } + .fetch_one(db) + .await + .map_err(|e| { + Error::InternalErr(format!( + "error during retrieval of has_failure_module: {e:#}" + )) + }) + .map(|v| v.unwrap_or(false)) +} diff --git a/backend/windmill-common/src/global_settings.rs b/backend/windmill-common/src/global_settings.rs index b0420ca248..a48db22b1f 100644 --- a/backend/windmill-common/src/global_settings.rs +++ b/backend/windmill-common/src/global_settings.rs @@ -29,6 +29,7 @@ pub const AUTOMATE_USERNAME_CREATION_SETTING: &str = "automate_username_creation pub const HUB_BASE_URL_SETTING: &str = "hub_base_url"; pub const HUB_ACCESSIBLE_URL_SETTING: &str = "hub_accessible_url"; pub const CRITICAL_ERROR_CHANNELS_SETTING: &str = "critical_error_channels"; +pub const CRITICAL_ALERT_MUTE_UI_SETTING: &str = "critical_alert_mute_ui"; pub const DEV_INSTANCE_SETTING: &str = "dev_instance"; pub const JWT_SECRET_SETTING: &str = "jwt_secret"; pub const EMAIL_DOMAIN_SETTING: &str = "email_domain"; diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index ad96594ac8..8eb57cb236 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -86,6 +86,8 @@ lazy_static::lazy_static! { pub static ref METRICS_ENABLED: AtomicBool = AtomicBool::new(std::env::var("METRICS_PORT").is_ok() || std::env::var("METRICS_ADDR").is_ok()); pub static ref METRICS_DEBUG_ENABLED: AtomicBool = AtomicBool::new(false); + pub static ref CRITICAL_ALERT_MUTE_UI_ENABLED: AtomicBool = AtomicBool::new(false); + pub static ref BASE_URL: Arc> = Arc::new(RwLock::new("".to_string())); pub static ref IS_READY: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 04f95a3406..18c0c93205 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -28,6 +28,9 @@ pub const DEFAULT_PER_PAGE: usize = 1000; pub const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version"); +use std::sync::atomic::Ordering; +use crate::CRITICAL_ALERT_MUTE_UI_ENABLED; + lazy_static::lazy_static! { pub static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new() .user_agent("windmill/beta") @@ -244,9 +247,12 @@ pub fn generate_lock_id(database_name: &str) -> i64 { pub async fn report_critical_error(error_message: String, _db: DB) -> () { tracing::error!("CRITICAL ERROR: {error_message}"); + let mute = CRITICAL_ALERT_MUTE_UI_ENABLED.load(Ordering::Relaxed); + if let Err(err) = sqlx::query!( - "INSERT INTO alerts (alert_type, message) VALUES ('critical_error', $1)", - error_message + "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('critical_error', $1, $2)", + error_message, + mute ) .execute(&_db) .await @@ -261,9 +267,12 @@ pub async fn report_critical_error(error_message: String, _db: DB) -> () { pub async fn report_recovered_critical_error(message: String, _db: DB) -> () { tracing::info!("RECOVERED CRITICAL ERROR: {message}"); + let mute = CRITICAL_ALERT_MUTE_UI_ENABLED.load(Ordering::Relaxed); + if let Err(err) = sqlx::query!( - "INSERT INTO alerts (alert_type, message) VALUES ('recovered_critical_error', $1)", - message + "INSERT INTO alerts (alert_type, message, acknowledged) VALUES ('recovered_critical_error', $1, $2)", + message, + mute ) .execute(&_db) .await diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 9b94c0f198..275034efe7 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -71,6 +71,9 @@ use windmill_common::BASE_URL; #[cfg(feature = "cloud")] use windmill_common::users::SUPERADMIN_SYNC_EMAIL; +#[cfg(feature = "enterprise")] +use windmill_common::flows::has_failure_module; + #[cfg(feature = "enterprise")] use windmill_common::worker::CLOUD_HOSTED; @@ -482,13 +485,6 @@ where } } -#[cfg(feature = "enterprise")] -#[derive(Deserialize)] -struct RawFlowFailureModule { - #[cfg(feature = "enterprise")] - failure_module: Option>, -} - #[instrument(level = "trace", skip_all)] pub async fn add_completed_job_error( db: &Pool, @@ -952,15 +948,7 @@ pub async fn add_completed_job< } else if !skip_downstream_error_handlers && (matches!(queued_job.job_kind, JobKind::Script) || matches!(queued_job.job_kind, JobKind::Flow) - && queued_job - .raw_flow - .as_ref() - .and_then(|v| { - serde_json::from_str::((**v).get()) - .ok() - .and_then(|v| v.failure_module) - }) - .is_none()) + && !has_failure_module(job_id, db, true).await.unwrap_or(false)) && queued_job.parent_job.is_none() { let result = serde_json::from_str( diff --git a/backend/windmill-worker/src/handle_child.rs b/backend/windmill-worker/src/handle_child.rs index 6846e9dfee..4d09c36abc 100644 --- a/backend/windmill-worker/src/handle_child.rs +++ b/backend/windmill-worker/src/handle_child.rs @@ -48,6 +48,7 @@ use futures::{ use crate::common::{resolve_job_timeout, OccupancyMetrics}; use crate::job_logger::{append_job_logs, append_with_limit, LARGE_LOG_THRESHOLD_SIZE}; +use crate::job_logger_ee::process_streaming_log_lines; use crate::{MAX_RESULT_SIZE, MAX_WAIT_FOR_SIGINT, MAX_WAIT_FOR_SIGTERM}; lazy_static::lazy_static! { @@ -434,6 +435,8 @@ pub async fn handle_child( } } + + async fn get_mem_peak(pid: Option, nsjail: bool) -> i32 { if pid.is_none() { return -1; @@ -689,19 +692,24 @@ fn child_joined_output_stream( let stdout = BufReader::new(stdout).lines(); let stderr = BufReader::new(stderr).lines(); - stream::select(lines_to_stream(stderr), lines_to_stream(stdout)) + stream::select(lines_to_stream(stderr, true), lines_to_stream(stdout, false)) } pub fn lines_to_stream( mut lines: tokio::io::Lines, + stderr: bool, ) -> impl futures::Stream> { stream::poll_fn(move |cx| { std::pin::Pin::new(&mut lines) .poll_next_line(cx) - .map(|result| result.transpose()) + .map(|result| { + process_streaming_log_lines(result, stderr) + }) }) } + + pub fn process_status(status: ExitStatus) -> error::Result<()> { if status.success() { Ok(()) diff --git a/backend/windmill-worker/src/job_logger.rs b/backend/windmill-worker/src/job_logger.rs index 05b9e5812b..f92ee16536 100644 --- a/backend/windmill-worker/src/job_logger.rs +++ b/backend/windmill-worker/src/job_logger.rs @@ -1,15 +1,6 @@ -use itertools::Itertools; -use swc_ecma_parser::lexer::util::CharExt; - -#[cfg(all(feature = "enterprise", feature = "parquet"))] -use object_store::path::Path; use regex::Regex; -#[cfg(all(feature = "enterprise", feature = "parquet"))] -use windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS; - -use windmill_common::error::{self}; -use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR}; +use windmill_common::worker::CLOUD_HOSTED; use windmill_queue::append_logs; @@ -19,6 +10,12 @@ use std::sync::Arc; use uuid::Uuid; use windmill_common::DB; +use crate::job_logger_ee::default_disk_log_storage; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +use crate::job_logger_ee::s3_storage; + + pub enum CompactLogs { #[cfg(not(all(feature = "enterprise", feature = "parquet")))] NotEE, @@ -28,150 +25,7 @@ pub enum CompactLogs { S3, } -async fn compact_logs( - job_id: Uuid, - w_id: &str, - db: &DB, - nlogs: String, - total_size: Arc, - compact_kind: CompactLogs, - _worker_name: &str, -) -> error::Result<(String, String)> { - let mut prev_logs = sqlx::query_scalar!( - "SELECT logs FROM job_logs WHERE job_id = $1 AND workspace_id = $2", - job_id, - w_id - ) - .fetch_optional(db) - .await? - .flatten() - .unwrap_or_default(); - let size = prev_logs.char_indices().count() as i32; - let nlogs_len = nlogs.char_indices().count(); - let to_keep_in_db = usize::max( - usize::min(nlogs_len, 3000), - nlogs_len % LARGE_LOG_THRESHOLD_SIZE, - ); - let extra_split = to_keep_in_db < nlogs_len; - let stored_in_storage_len = if extra_split { - nlogs_len - to_keep_in_db - } else { - 0 - }; - let extra_to_newline = nlogs - .chars() - .skip(stored_in_storage_len) - .find_position(|x| x.is_line_break()) - .map(|(i, _)| i) - .unwrap_or(to_keep_in_db); - let stored_in_storage_to_newline = stored_in_storage_len + extra_to_newline; - let (append_to_storage, stored_in_db) = if extra_split { - if stored_in_storage_to_newline == nlogs.len() { - (nlogs.as_ref(), "".to_string()) - } else { - let split_idx = nlogs - .char_indices() - .nth(stored_in_storage_to_newline) - .map(|(i, _)| i) - .unwrap_or(0); - let (append_to_storage, stored_in_db) = nlogs.split_at(split_idx); - // tracing::error!("{append_to_storage} ||||| {stored_in_db}"); - // tracing::error!( - // "{:?} {:?} {} {}", - // excess_prev_logs.lines().last(), - // current_logs.lines().next(), - // split_idx, - // excess_size_modulo - // ); - (append_to_storage, stored_in_db.to_string()) - } - } else { - // tracing::error!("{:?}", nlogs.lines().last()); - ("", nlogs.to_string()) - }; - - let new_size_with_excess = size + stored_in_storage_to_newline as i32; - - let new_size = total_size.fetch_add( - new_size_with_excess as u32, - std::sync::atomic::Ordering::SeqCst, - ) + new_size_with_excess as u32; - - let path = format!( - "logs/{job_id}/{}_{new_size}.txt", - chrono::Utc::now().timestamp_millis() - ); - - let mut new_current_logs = match compact_kind { - CompactLogs::NoS3 => format!("\n[windmill] No object storage set in instance settings. Previous logs have been saved to disk at {path}"), - CompactLogs::S3 => format!("\n[windmill] Previous logs have been saved to object storage at {path}"), - #[cfg(not(all(feature = "enterprise", feature = "parquet")))] - CompactLogs::NotEE => format!("\n[windmill] Previous logs have been saved to disk at {path}"), - }; - new_current_logs.push_str(&stored_in_db); - - sqlx::query!( - "UPDATE job_logs SET logs = $1, log_offset = $2, - log_file_index = array_append(coalesce(log_file_index, array[]::text[]), $3) - WHERE workspace_id = $4 AND job_id = $5", - new_current_logs, - new_size as i32, - path, - w_id, - job_id - ) - .execute(db) - .await?; - prev_logs.push_str(&append_to_storage); - - return Ok((prev_logs, path)); -} - -async fn default_disk_log_storage( - job_id: Uuid, - w_id: &str, - db: &DB, - nlogs: String, - total_size: Arc, - compact_kind: CompactLogs, - worker_name: &str, -) { - match compact_logs( - job_id, - &w_id, - &db, - nlogs, - total_size, - compact_kind, - worker_name, - ) - .await - { - Err(e) => tracing::error!("Could not compact logs for job {job_id}: {e:?}",), - Ok((prev_logs, path)) => { - let path = format!("{}/{}", TMP_DIR, path); - let splitted = &path.split("/").collect_vec(); - tokio::fs::create_dir_all(splitted.into_iter().take(splitted.len() - 1).join("/")) - .await - .map_err(|e| { - tracing::error!("Could not create logs directory: {e:?}",); - e - }) - .ok(); - let created = tokio::fs::File::create(&path).await; - if let Err(e) = created { - tracing::error!("Could not create logs file {path}: {e:?}",); - return; - } - if let Err(e) = tokio::fs::write(&path, prev_logs).await { - tracing::error!("Could not write to logs file {path}: {e:?}"); - } else { - tracing::info!("Logs length of {job_id} has exceeded a threshold. Previous logs have been saved to disk at {path}"); - } - } - } -} pub(crate) async fn append_job_logs( job_id: Uuid, @@ -184,43 +38,7 @@ pub(crate) async fn append_job_logs( ) -> () { if must_compact_logs { #[cfg(all(feature = "enterprise", feature = "parquet"))] - if let Some(os) = OBJECT_STORE_CACHE_SETTINGS.read().await.clone() { - match compact_logs( - job_id, - &w_id, - &db, - logs, - total_size, - CompactLogs::S3, - &worker_name, - ) - .await - { - Err(e) => tracing::error!("Could not compact logs for job {job_id}: {e:?}",), - Ok((prev_logs, path)) => { - tracing::info!("Logs length of {job_id} has exceeded a threshold. Previous logs have been saved to object storage at {path}"); - let path2 = path.clone(); - if let Err(e) = os - .put(&Path::from(path), prev_logs.to_string().into_bytes().into()) - .await - { - tracing::error!("Could not save logs to s3: {e:?}"); - } - tracing::info!("Logs of {job_id} saved to object storage at {path2}"); - } - } - } else { - default_disk_log_storage( - job_id, - &w_id, - &db, - logs, - total_size, - CompactLogs::NoS3, - &worker_name, - ) - .await; - } + s3_storage(job_id, &w_id, &db, logs, total_size, &worker_name).await; #[cfg(not(all(feature = "enterprise", feature = "parquet")))] { @@ -252,6 +70,7 @@ pub fn append_with_limit(dst: &mut String, src: &str, limit: &mut usize) { if *NO_LOGS_AT_ALL { return; } + let src_str; let src = { src_str = RE_00.replace_all(src, ""); diff --git a/backend/windmill-worker/src/job_logger_ee.rs b/backend/windmill-worker/src/job_logger_ee.rs new file mode 100644 index 0000000000..e0758b31f3 --- /dev/null +++ b/backend/windmill-worker/src/job_logger_ee.rs @@ -0,0 +1,30 @@ +use std::io; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; + +use uuid::Uuid; +use windmill_common::DB; + +use crate::job_logger::CompactLogs; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +pub(crate) async fn s3_storage(_job_id: Uuid, _w_id: &String, _db: &sqlx::Pool, _logs: &String, _total_size: &Arc, _worker_name: &String) { + tracing::info!("Logs length of {job_id} has exceeded a threshold. Implementation to store excess on s3 in not OSS"); +} + +pub(crate) async fn default_disk_log_storage( + job_id: Uuid, + _w_id: &str, + _db: &DB, + _nlogs: String, + _total_size: Arc, + _compact_kind: CompactLogs, + _worker_name: &str, +) { + tracing::info!("Logs length of {job_id} has exceeded a threshold. Implementation to store excess on disk in not OSS"); +} + + +pub(crate) fn process_streaming_log_lines(r: Result, io::Error>, _stderr: bool) -> Option> { + r.transpose() +} \ No newline at end of file diff --git a/backend/windmill-worker/src/lib.rs b/backend/windmill-worker/src/lib.rs index b790eb3faf..680f3ca178 100644 --- a/backend/windmill-worker/src/lib.rs +++ b/backend/windmill-worker/src/lib.rs @@ -29,7 +29,7 @@ mod rust_executor; mod worker; mod worker_flow; mod worker_lockfiles; - +mod job_logger_ee; pub use worker::*; pub use result_processor::handle_job_error; diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index 112b3c84ca..9db4ed529b 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -199,8 +199,11 @@ pub async fn uv_pip_compile( .clone() .map(handle_ephemeral_token); if let Some(url) = pip_extra_index_url.as_ref() { - args.extend(["--extra-index-url", url, "--no-emit-index-url"]); - pip_args.push(format!("--extra-index-url {}", url)); + url.split(",").for_each(|url| { + args.extend(["--extra-index-url", url]); + pip_args.push(format!("--extra-index-url {}", url)); + }); + args.push("--no-emit-index-url"); } let pip_index_url = PIP_INDEX_URL .read() @@ -279,7 +282,9 @@ pub async fn uv_pip_compile( .clone() .map(handle_ephemeral_token); if let Some(url) = pip_extra_index_url.as_ref() { - args.extend(["--extra-index-url", url]); + url.split(",").for_each(|url| { + args.extend(["--extra-index-url", url]); + }); } let pip_index_url = PIP_INDEX_URL .read() @@ -1167,7 +1172,9 @@ pub async fn handle_python_reqs( .map(handle_ephemeral_token); if let Some(url) = pip_extra_index_url.as_ref() { - command_args.extend(["--extra-index-url", url]); + url.split(",").for_each(|url| { + command_args.extend(["--extra-index-url", url]); + }); } let pip_index_url = PIP_INDEX_URL .read() diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index a1a4fd9904..5421b502e4 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -49,7 +49,7 @@ use windmill_common::{ Approval, BranchAllStatus, BranchChosen, FlowStatus, FlowStatusModule, RetryStatus, MAX_RETRY_ATTEMPTS, MAX_RETRY_INTERVAL, }, - flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform, Retry, Suspend}, + flows::{has_failure_module, FlowModule, FlowModuleValue, FlowValue, InputTransform, Retry, Suspend}, }; use windmill_queue::schedule::get_schedule_opt; use windmill_queue::{ @@ -963,7 +963,7 @@ pub async fn update_flow_status_after_job_completion_internal< false if !is_failure_step && !skip_error_handler - && has_failure_module(flow, db).await? => + && has_failure_module(flow, db, false).await? => { true } @@ -1290,23 +1290,6 @@ async fn compute_skip_branchall_failure<'c>( })) } -async fn has_failure_module<'c>(flow: Uuid, db: &DB) -> Result { - sqlx::query_scalar::<_, Option>( - "SELECT raw_flow->'failure_module' != 'null'::jsonb - FROM queue - WHERE id = $1", - ) - .bind(flow) - .fetch_one(db) - .await - .map_err(|e| { - Error::InternalErr(format!( - "error during retrieval of has_failure_module: {e:#}" - )) - }) - .map(|v| v.unwrap_or(false)) -} - // async fn retrieve_cleanup_module<'c>(flow_uuid: Uuid, db: &DB) -> Result { // tracing::warn!("Retrieving cleanup module of flow {}", flow_uuid); // let raw_value = sqlx::query_scalar!( diff --git a/benchmarks/benchmark_oneoff.ts b/benchmarks/benchmark_oneoff.ts index 24ba4e2c13..41321b3ec6 100644 --- a/benchmarks/benchmark_oneoff.ts +++ b/benchmarks/benchmark_oneoff.ts @@ -8,6 +8,7 @@ import { DenoLandProvider } from "https://deno.land/x/cliffy@v0.25.7/command/upg 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"; import { VERSION, createBenchScript, getFlowPayload, login } from "./lib.ts"; @@ -172,8 +173,8 @@ export async function main({ kind: "script", path: "f/benchmarks/" + kind, }); - } else if (["2steps"].includes(kind)) { - nStepsFlow = 2; + } else if (["2steps", "bigscriptinflow"].includes(kind)) { + nStepsFlow = kind == "2steps" ? 2 : 1; const payload = getFlowPayload(kind); body = JSON.stringify({ kind: "flow", @@ -194,6 +195,15 @@ export async function main({ kind: "script", path: kind.substr(7), }); + } else if (kind == "bigrawscript") { + noVerify = true; + body = JSON.stringify({ + kind: "rawscript", + rawscript: { + language: api.RawScript.language.BASH, + content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(25000) + "echo \"$WM_FLOW_JOB_ID\"\n", + }, + }); } else { throw new Error("Unknown script pattern " + kind); } @@ -246,7 +256,7 @@ export async function main({ } else { const elapsed = start ? Date.now() - start : 0; completedJobs = await getCompletedJobsCount(); - if (kind === "2steps" || kind.startsWith("flow:")) { + if (nStepsFlow > 0) { completedJobs = Math.floor(completedJobs / (nStepsFlow + 1)); } const avgThr = ((completedJobs / elapsed) * 1000).toFixed(2); diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 5c02fcf14d..d59c074be2 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.421.2"; +export const VERSION = "v1.423.2"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ @@ -235,6 +235,23 @@ export const getFlowPayload = (flowPattern: string): api.FlowPreview => { ], }, }; + } else if (flowPattern == "bigscriptinflow") { + return { + path: "bigscriptinflow", + args: {}, + value: { + modules: [ + { + value: { + input_transforms: {}, + language: api.RawScript.language.BASH, + type: "rawscript", + content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(25000) + "echo \"$WM_FLOW_JOB_ID\"\n", + }, + } + ], + } + }; } else { return { path: "2steps", diff --git a/benchmarks/suite_config.json b/benchmarks/suite_config.json index 568402ca2d..7619d4d3b9 100644 --- a/benchmarks/suite_config.json +++ b/benchmarks/suite_config.json @@ -30,5 +30,13 @@ { "kind": "nativets", "jobs": 2000 + }, + { + "kind": "bigrawscript", + "jobs": 10000 + }, + { + "kind": "bigscriptinflow", + "jobs": 10000 } ] \ No newline at end of file diff --git a/cli/main.ts b/cli/main.ts index 38a4087b01..c35b0c7a27 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -60,7 +60,7 @@ export { // } // }); -export const VERSION = "1.421.2"; +export const VERSION = "1.423.2"; const command = new Command() .name("wmill") diff --git a/flake.nix b/flake.nix index 28e0816667..2c1845d176 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,10 @@ ]); in { devShell = pkgs.mkShell { - buildInputs = buildInputs ++ (with pkgs; [ git go xcaddy sqlx-cli ]); + buildInputs = buildInputs ++ (with pkgs; [ + git xcaddy sqlx-cli flock rust-analyzer + deno python3 python3Packages.pip go bun uv + ]); packages = [ (pkgs.writeScriptBin "wm-caddy" '' cd ./frontend @@ -56,6 +59,9 @@ sqlx database create wm-migrate '') + (pkgs.writeScriptBin "wm-bench" '' + deno run -A benchmarks/main.ts -e admin@windmill.dev -p changeme $* + '') (pkgs.writeScriptBin "wm" '' cd ./frontend npm run dev $* @@ -68,6 +74,12 @@ DATABASE_URL = "postgres://postgres:changeme@127.0.0.1:5432/"; REMOTE = "http://127.0.0.1:8000"; REMOTE_LSP = "http://127.0.0.1:3001"; + DENO_PATH = "${pkgs.deno}/bin/deno"; + PYTHON_PATH = "${pkgs.python3}/bin/python3"; + GO_PATH = "${pkgs.go}/bin/go"; + BUN_PATH = "${pkgs.bun}/bin/bun"; + UV_PATH = "${pkgs.uv}/bin/uv"; + FLOCK_PATH = "${pkgs.flock}/bin/flock"; }; packages.default = self.packages.${system}.windmill; packages.windmill-client = pkgs.stdenv.mkDerivation { @@ -82,7 +94,7 @@ npm config set strict-ssl false cd frontend npm install --verbose - npm run generate-backend-client-mac + npm run ${if pkgs.stdenv.isDarwin then "generate-backend-client-mac" else "generate-backend-client"} NODE_OPTIONS="--max-old-space-size=8192" npm run build ''; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d3ff5df966..2b1bd0e614 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.421.2", + "version": "1.423.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.421.2", + "version": "1.423.2", "license": "AGPL-3.0", "dependencies": { "@aws-crypto/sha256-js": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index 3b10b586cd..4465bf84b5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.421.2", + "version": "1.423.2", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index bbaf166e0d..ef20ebdb35 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -418,6 +418,9 @@ bind:selected={value} options={itemsType?.multiselect ?? []} selectedOptionsDraggable={true} + on:open={() => { + dispatch('focus') + }} /> {:else if itemsType?.enum != undefined && Array.isArray(itemsType?.enum) && Array.isArray(value)} @@ -428,6 +431,9 @@ bind:selected={value} options={itemsType?.enum ?? []} selectedOptionsDraggable={true} + on:open={() => { + dispatch('focus') + }} /> {:else} @@ -816,6 +822,12 @@ bind:value {enum_} {autofocus} + on:focus={() => { + dispatch('focus') + }} + on:blur={(e) => { + dispatch('blur') + }} enumLabels={extra['enumLabels']} /> @@ -884,14 +896,16 @@
{#if password || extra?.['password'] == true} - {#if value && typeof value == 'string' && value?.startsWith('$var:')} - - {:else if onlyMaskPassword} - + {#if onlyMaskPassword} + {#if value && typeof value == 'string' && value?.startsWith('$var:')} + + {:else} + + {/if} {:else} {/if} diff --git a/frontend/src/lib/components/FlowGraphViewer.svelte b/frontend/src/lib/components/FlowGraphViewer.svelte index 1ecef2943f..326e1275b6 100644 --- a/frontend/src/lib/components/FlowGraphViewer.svelte +++ b/frontend/src/lib/components/FlowGraphViewer.svelte @@ -7,6 +7,7 @@ import FlowGraphViewerStep from './FlowGraphViewerStep.svelte' import FlowGraphV2 from './graph/FlowGraphV2.svelte' import { dfs } from './flows/dfs' + import { workspaceStore } from '$lib/stores' export let flow: { summary: string @@ -22,6 +23,7 @@ export let noGraph = false export let triggerNode = false export let stepDetail: FlowModule | string | undefined = undefined + export let workspace: string | undefined = $workspaceStore const dispatch = createEventDispatcher() @@ -37,6 +39,7 @@ path={flow?.path} {download} minHeight={400} + {workspace} modules={flow?.value?.modules} failureModule={flow?.value?.failure_module} preprocessorModule={flow?.value?.preprocessor_module} diff --git a/frontend/src/lib/components/FlowStatusViewerInner.svelte b/frontend/src/lib/components/FlowStatusViewerInner.svelte index ec6982a5df..2c9aa54461 100644 --- a/frontend/src/lib/components/FlowStatusViewerInner.svelte +++ b/frontend/src/lib/components/FlowStatusViewerInner.svelte @@ -29,6 +29,7 @@ import Alert from './common/alert/Alert.svelte' import FlowGraphViewerStep from './FlowGraphViewerStep.svelte' import FlowGraphV2 from './graph/FlowGraphV2.svelte' + import { buildPrefix } from './graph/graphBuilder' const dispatch = createEventDispatcher() @@ -74,6 +75,10 @@ export let wideResults = false export let hideFlowResult = false + export let workspace: string | undefined = $workspaceStore + export let prefix: string | undefined = undefined + export let subflowParentsGlobalModuleStates: Writable>[] = [] + export let subflowParentsDurationStatuses: Writable>[] = [] let jobResults: any[] = flowJobIds?.flowJobs?.map((x, id) => `iter #${id + 1} not loaded by frontend yet`) ?? [] @@ -83,6 +88,7 @@ let localModuleStates: Writable> = writable({}) let localDurationStatuses: Writable> = writable({}) + let expandedSubflows: Record = {} export let job: Job | undefined = undefined @@ -94,6 +100,24 @@ // } // } + function updateModuleStates( + moduleState: Writable>, + key: string, + newValue: GraphModuleState, + keepType: boolean | undefined + ) { + moduleState.update((x) => { + if (keepType && (x[key]?.type == 'Success' || x[key]?.type == 'Failure')) { + newValue.type = x[key].type + } + x[key] = newValue + return x + }) + } + + function buildSubflowKey(key: string, prefix: string | undefined) { + return prefix ? 'subflow:' + prefix + key : key + } function setModuleState( key: string, value: Partial, @@ -102,15 +126,14 @@ ) { let newValue = { ...($localModuleStates[key] ?? {}), ...value } if (!deepEqual($localModuleStates[key], value) || force) { - ;[localModuleStates, ...globalModuleStates].forEach((s) => { - s.update((x) => { - if (keepType && (x[key]?.type == 'Success' || x[key]?.type == 'Failure')) { - newValue.type = x[key].type - } - x[key] = newValue - return x - }) - }) + ;[localModuleStates, ...globalModuleStates].forEach((s) => + updateModuleStates(s, key, newValue, keepType) + ) + if (prefix) { + subflowParentsGlobalModuleStates.forEach((s) => + updateModuleStates(s, buildSubflowKey(key, prefix), newValue, keepType) + ) + } } } @@ -124,6 +147,14 @@ return x }) }) + if (prefix) { + subflowParentsDurationStatuses.forEach((s) => { + s.update((x) => { + x[buildSubflowKey(key, prefix)].byJob[id] = value + return x + }) + }) + } } } @@ -131,15 +162,25 @@ if ($localDurationStatuses[modId] == undefined) { $localDurationStatuses[modId] = { byJob: {} } } - let prefixed = modId globalDurationStatuses.forEach((x) => x.update((x) => { - if (x[prefixed] == undefined) { - x[prefixed] = { byJob: {} } + if (x[modId] == undefined) { + x[modId] = { byJob: {} } } return x }) ) + if (prefix) { + subflowParentsDurationStatuses.forEach((x) => + x.update((x) => { + let key = buildSubflowKey(modId, prefix) + if (x[key] == undefined) { + x[key] = { byJob: {} } + } + return x + }) + ) + } } let innerModules: FlowStatusModule[] = [] @@ -368,7 +409,7 @@ if (globalRefreshes) { let modId = flowJobIds?.moduleId if (modId) { - globalRefreshes[modId] = async (loopJob) => { + globalRefreshes[buildSubflowKey(modId, prefix)] = async (loopJob) => { setIteration(loopJob.index, loopJob.job, false, modId ?? '') refresh(true, loopJob) } @@ -631,6 +672,36 @@ }) } } + + function allModulesForTimeline( + modules: FlowModule[], + expandedSubflows: Record + ): string[] { + const ids = dfs(modules, (x) => x.id) + + function rec(ids: string[], prefix: string | undefined): string[] { + return ids.concat( + ids.flatMap((id) => { + let fms = expandedSubflows[id] + let oid = id.split(':').pop() + if (!oid) { + return [] + } + let nprefix = buildPrefix(prefix, oid) + return fms + ? rec( + dfs(fms, (x) => + x.id.startsWith('subflow:') ? x.id : buildSubflowKey(x.id, nprefix) + ), + nprefix + ) + : [] + }) + ) + } + + return rec(ids, undefined) + } {#if notAnonynmous} @@ -803,6 +874,9 @@ job={storedListJobs[j]} globalModuleStates={[localModuleStates, ...globalModuleStates]} globalDurationStatuses={[localDurationStatuses, ...globalDurationStatuses]} + {prefix} + {subflowParentsGlobalModuleStates} + {subflowParentsDurationStatuses} render={forloop_selected == loopJobId && selected == 'sequence' && render} reducedPolling={flowJobIds?.flowJobs.length && flowJobIds?.flowJobs.length > 20} {workspaceId} @@ -880,6 +954,9 @@ {childFlow} globalModuleStates={[localModuleStates, ...globalModuleStates]} globalDurationStatuses={[localDurationStatuses, ...globalDurationStatuses]} + {prefix} + {subflowParentsGlobalModuleStates} + {subflowParentsDurationStatuses} render={failedRetry == retry_selected && render} reducedPolling={false} {workspaceId} @@ -895,6 +972,17 @@ bind:refresh={recursiveRefresh[mod.job ?? '']} globalModuleStates={[]} globalDurationStatuses={[]} + prefix={buildPrefix(prefix, mod.id ?? '')} + subflowParentsGlobalModuleStates={[ + localModuleStates, + ...globalModuleStates, + ...subflowParentsGlobalModuleStates + ]} + subflowParentsDurationStatuses={[ + localDurationStatuses, + ...globalDurationStatuses, + ...subflowParentsDurationStatuses + ]} render={selected == 'sequence' && render} {workspaceId} jobId={mod.job} @@ -915,6 +1003,9 @@ globalDurationStatuses={[localDurationStatuses, ...globalDurationStatuses]} render={selected == 'sequence' && render} {workspaceId} + {prefix} + {subflowParentsGlobalModuleStates} + {subflowParentsDurationStatuses} jobId={mod.job} innerModule={mod.flow_jobs ? job.raw_flow?.modules[i]?.value : undefined} flowJobIds={mod.flow_jobs @@ -973,6 +1064,7 @@ minHeight={wrapperHeight} success={jobId != undefined && isSuccess(job?.['success'])} flowModuleStates={$localModuleStates} + bind:expandedSubflows on:select={(e) => { if (rightColumnSelect != 'node_definition') { rightColumnSelect = 'node_status' @@ -1004,11 +1096,13 @@ selectedForloopIndex: detail.index }) globalRefreshes[detail.moduleId]?.({ job: detail.id, index: detail.index }) + // console.log('selectedIteration', prefix, detail.moduleId, globalRefreshes) }} modules={job.raw_flow?.modules ?? []} failureModule={job.raw_flow?.failure_module} preprocessorModule={job.raw_flow?.preprocessor_module} allowSimplifiedPoll={false} + {workspace} />
x.id)} + flowModules={allModulesForTimeline( + job?.raw_flow?.modules ?? [], + expandedSubflows ?? {} + )} durationStatuses={localDurationStatuses} /> {:else if rightColumnSelect == 'node_status'} diff --git a/frontend/src/lib/components/FlowTimeline.svelte b/frontend/src/lib/components/FlowTimeline.svelte index 3f918c8c6b..4027eca9c3 100644 --- a/frontend/src/lib/components/FlowTimeline.svelte +++ b/frontend/src/lib/components/FlowTimeline.svelte @@ -178,7 +178,7 @@ {/if}
-
{k}
+
{k.startsWith('subflow:') ? k.substring(8) : k}
{#if min && total}
diff --git a/frontend/src/lib/components/InputTransformForm.svelte b/frontend/src/lib/components/InputTransformForm.svelte index 07a7ee534d..aa0c31057b 100644 --- a/frontend/src/lib/components/InputTransformForm.svelte +++ b/frontend/src/lib/components/InputTransformForm.svelte @@ -238,7 +238,7 @@ setPropertyType(arg?.value) codeInjectionDetected = checkCodeInjection(arg?.value) != undefined } else if (propertyType == 'javascript' && focused) { - setPropertyType(arg?.expr) + // setPropertyType(arg?.expr) $inputMatches = checkCodeInjection(arg?.expr) } } @@ -279,7 +279,7 @@ class={twMerge( 'pl-2 pt-2 pb-2 ml-2 relative hover:bg-surface hover:shadow-md transition-all duration-200', $propPickerConfig?.propName == argName - ? 'bg-surface border-l-4 border-blue-500 shadow-md rounded-l-md z-2000' + ? 'bg-surface border-l-4 border-blue-500 shadow-md rounded-l-md z-50 ' : 'hover:rounded-md', $$props.class )} @@ -565,10 +565,13 @@
{#if $propPickerConfig?.propName == argName} -
+
0} {#each keys as argName (argName)} {#if (!filter || filter.includes(argName)) && Object.keys(schema.properties ?? {}).includes(argName)} -
+
+ | { + allowed_resources: string[] + allow_user_resources: boolean + allow_workspace_resource: boolean + file_key_regex: string + } + | undefined) + | undefined = undefined + export let workspace: string | undefined = undefined let oneOfSelected: string | undefined = undefined async function updateOneOfSelected(oneOf: SchemaProperty[] | undefined) { @@ -428,6 +440,9 @@ .toLowerCase() == 's3object'}
{ diff --git a/frontend/src/lib/components/LightweightSchemaForm.svelte b/frontend/src/lib/components/LightweightSchemaForm.svelte index d4da597066..55b1373b4d 100644 --- a/frontend/src/lib/components/LightweightSchemaForm.svelte +++ b/frontend/src/lib/components/LightweightSchemaForm.svelte @@ -16,6 +16,18 @@ export let defaultValues: Record = {} export let dynamicEnums: Record = {} export let disabled: boolean = false + export let appPath: string | undefined = undefined + export let computeS3ForceViewerPolicies: + | (() => + | { + allowed_resources: string[] + allow_user_resources: boolean + allow_workspace_resource: boolean + file_key_regex: string + } + | undefined) + | undefined = undefined + export let workspace: string | undefined = undefined let inputCheck: { [id: string]: boolean } = {} let errors: { [id: string]: string } = {} @@ -104,6 +116,9 @@ {displayType} {css} disabled={disabled || schema.properties[argName].disabled} + {appPath} + {computeS3ForceViewerPolicies} + {workspace} /> {/if} {/each} diff --git a/frontend/src/lib/components/PasswordArgInput.svelte b/frontend/src/lib/components/PasswordArgInput.svelte index 9b87e8843b..c9250eab6b 100644 --- a/frontend/src/lib/components/PasswordArgInput.svelte +++ b/frontend/src/lib/components/PasswordArgInput.svelte @@ -10,41 +10,53 @@ let path = '' let password = value && !value.startsWith('$var:') ? value : '' + let isGenerating = false async function generateValue() { - let npath = - 'u/' + - ($userStore?.username ?? $userStore?.email)?.split('@')[0] + - '/secret_arg/' + - generateRandomString(12) - let nvalue = '$var:' + npath - await VariableService.createVariable({ - workspace: $workspaceStore!, - requestBody: { - value: password, - is_secret: true, - path: npath, - description: '', - expires_at: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString() - } - }) - path = npath - value = nvalue + if (isGenerating) return + isGenerating = true + try { + let npath = + 'u/' + + ($userStore?.username ?? $userStore?.email)?.split('@')[0] + + '/secret_arg/' + + generateRandomString(12) + let nvalue = '$var:' + npath + await VariableService.createVariable({ + workspace: $workspaceStore!, + requestBody: { + value: password, + is_secret: true, + path: npath, + description: 'Ephemeral secret variable', + expires_at: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString() + } + }) + path = npath + value = nvalue + debouncedUpdate() + } finally { + isGenerating = false + } } async function updateValue() { - await VariableService.updateVariable({ - workspace: $workspaceStore!, - path: path, - requestBody: { - value: password - } - }) + try { + await VariableService.updateVariable({ + workspace: $workspaceStore!, + path: path, + requestBody: { + value: password + } + }) + } catch (e) { + generateValue() + } } let timeout: NodeJS.Timeout | undefined = undefined function debouncedUpdate() { timeout && clearTimeout(timeout) - setTimeout(updateValue, 500) + timeout = setTimeout(updateValue, 500) } $: password && debouncedUpdate() diff --git a/frontend/src/lib/components/SuperadminSettings.svelte b/frontend/src/lib/components/SuperadminSettings.svelte index 1e81ace411..1670a51086 100644 --- a/frontend/src/lib/components/SuperadminSettings.svelte +++ b/frontend/src/lib/components/SuperadminSettings.svelte @@ -23,6 +23,7 @@ import { isCloudHosted } from '$lib/cloud' import InstanceNameEditor from './InstanceNameEditor.svelte' import Toggle from './Toggle.svelte' + import { instanceSettingsSelectedTab } from '$lib/stores'; let drawer: Drawer let filter = '' @@ -56,6 +57,9 @@ let tab: 'users' | string = 'users' + $: $instanceSettingsSelectedTab, tab = $instanceSettingsSelectedTab + $: tab, instanceSettingsSelectedTab.set(tab) + let nbDisplayed = 50 let instanceSettings diff --git a/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte b/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte index fcbd4e789a..a77d708e13 100644 --- a/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte +++ b/frontend/src/lib/components/apps/components/buttons/AppSchemaForm.svelte @@ -18,6 +18,9 @@ import ResolveConfig from '../helpers/ResolveConfig.svelte' import ResolveStyle from '../helpers/ResolveStyle.svelte' import { deepEqual } from 'fast-equals' + import { computeWorkspaceS3FileInputPolicy } from '../../editor/appUtilsS3' + import { defaultIfEmptyString } from '$lib/utils' + import { userStore } from '$lib/stores' export let id: string export let componentInput: AppInput | undefined @@ -26,8 +29,16 @@ export let configuration: RichConfigurations export let customCss: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined - const { worldStore, connectingInput, app, selectedComponent, componentControl } = - getContext('AppViewerContext') + const { + worldStore, + connectingInput, + app, + selectedComponent, + componentControl, + appPath, + isEditor, + workspace + } = getContext('AppViewerContext') const iterContext = getContext('ListWrapperContext') const listInputs: ListInputs | undefined = getContext('ListInputs') @@ -100,6 +111,14 @@ previousDefault = structuredClone(resolvedConfig.defaultValues) args = previousDefault ?? {} } + + function computeS3ForceViewerPolicies() { + if (!isEditor) { + return undefined + } + const policy = computeWorkspaceS3FileInputPolicy() + return policy + } {#each Object.keys(components['schemaformcomponent'].initialData.configuration) as key (key)} @@ -140,6 +159,9 @@ bind:this={schemaForm} displayType={Boolean(resolvedConfig.displayType)} largeGap={Boolean(resolvedConfig.largeGap)} + appPath={defaultIfEmptyString(appPath, `u/${$userStore?.username ?? 'unknown'}/newapp`)} + {computeS3ForceViewerPolicies} + {workspace} {css} />
diff --git a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte index 66fd746285..286c5ac573 100644 --- a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte +++ b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte @@ -26,6 +26,7 @@ import { get } from 'svelte/store' import RefreshButton from '$lib/components/apps/components/helpers/RefreshButton.svelte' import { ctxRegex } from '../../utils' + import { computeWorkspaceS3FileInputPolicy } from '../../editor/appUtilsS3' // Component props export let id: string @@ -671,6 +672,14 @@ return undefined } } + + function computeS3ForceViewerPolicies() { + if (!isEditor) { + return undefined + } + const policy = computeWorkspaceS3FileInputPolicy() + return policy + } {#each Object.entries(fields ?? {}) as [key, v] (key)} @@ -754,6 +763,9 @@
('AppViewerContext') + function computeForceViewerPolicies() { + if (!isEditor) { + return undefined + } + const policy = computeS3FileInputPolicy((configuration as any)?.type?.configuration?.s3, $app) + return policy + } @@ -139,5 +151,7 @@ outputs.result.set(value) }} {forceDisplayUploads} + appPath={defaultIfEmptyString(appPath, `u/${$userStore?.username ?? 'unknown'}/newapp`)} + {computeForceViewerPolicies} /> {/if} diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index af2f4563df..662631f752 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -87,6 +87,7 @@ import ToggleEnable from '$lib/components/common/toggleButton-v2/ToggleEnable.svelte' import HideButton from './settingsPanel/HideButton.svelte' import DeployOverrideConfirmationModal from '$lib/components/common/confirmationModal/DeployOverrideConfirmationModal.svelte' + import { computeS3FileInputPolicy, computeWorkspaceS3FileInputPolicy } from './appUtilsS3' async function hash(message) { try { @@ -195,8 +196,12 @@ } async function computeTriggerables() { + const items = allItems($app.grid, $app.subgrids) + + console.log('items', items) + const allTriggers: ([string, TriggerableV2] | undefined)[] = (await Promise.all( - allItems($app.grid, $app.subgrids) + items .flatMap((x) => { let c = x.data as AppComponent let r: { input: AppInput | undefined; id: string }[] = [ @@ -296,6 +301,44 @@ allTriggers.filter(Boolean) as [string, TriggerableV2][] ) policy.triggerables_v2 = ntriggerables + + const s3_inputs = items + .filter((x) => (x.data as AppComponent).type === 's3fileinputcomponent') + .map((x) => { + const c = x.data as AppComponent + const config = c.configuration as any + return computeS3FileInputPolicy(config?.type?.configuration?.s3, $app) + }) + .filter(Boolean) as { + allowed_resources: string[] + allow_user_resources: boolean + file_key_regex: string + }[] + + if ( + items.findIndex((x) => { + const c = x.data as AppComponent + if (c.type === 'schemaformcomponent') { + return ( + Object.values((c.componentInput as any)?.value?.properties ?? {}).findIndex( + (p: any) => p?.type === 'object' && p?.format === 'resource-s3_object' + ) !== -1 + ) + } else if (c.type === 'formbuttoncomponent' || c.type === 'formcomponent') { + return ( + Object.values((c.componentInput as any)?.fields ?? {}).findIndex( + (p: any) => p?.fieldType === 'object' && p?.format === 'resource-s3_object' + ) !== -1 + ) + } else { + return false + } + }) !== -1 + ) { + s3_inputs.push(computeWorkspaceS3FileInputPolicy()) + } + + policy.s3_inputs = s3_inputs } async function processRunnable( diff --git a/frontend/src/lib/components/apps/editor/appUtilsS3.ts b/frontend/src/lib/components/apps/editor/appUtilsS3.ts new file mode 100644 index 0000000000..2256331cc4 --- /dev/null +++ b/frontend/src/lib/components/apps/editor/appUtilsS3.ts @@ -0,0 +1,82 @@ +import type { AppInput, EvalInputV2 } from '../inputType' +import type { App } from '../types' +import { collectOneOfFields } from './appUtils' + +function filenameExprToRegex(template: string) { + const filenameEscaped = template.replaceAll('${file.name}', '') // replace filename with placeholder + const escapedTemplate = filenameEscaped + .slice(1, -1) // remove quotes + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // escape regex special characters + const regexPattern = escapedTemplate.replaceAll('', '[^/]+') // replace filename placeholder with regex pattern + return `^${regexPattern}$` +} + +function staticToRegex(str: string) { + return `^${str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$` +} + +function checkIfExprIsString(input: string) { + return /^(['"`])[^'"`]*\1$/g.test(input) +} + +function checkIfEvalIsStringWithFilename(input: EvalInputV2) { + if (input.connections.length > 0) { + return false + } else { + return checkIfExprIsString(input.expr.replaceAll('${file.name}', '')) + } +} + +function removeResourcePrefix(resource: string) { + return resource.replace(/^\$res:/, '') +} + +export function computeWorkspaceS3FileInputPolicy() { + return { + allow_workspace_resource: true, + allowed_resources: [], + allow_user_resources: false, + file_key_regex: '' + } +} + +export function computeS3FileInputPolicy(s3Config: any, app: App) { + const resourceInput = s3Config?.resource as AppInput | undefined + const pathTemplateInput = s3Config?.pathTemplate as AppInput | undefined + + const allow_workspace_resource = + !resourceInput || (resourceInput.type === 'static' && !resourceInput.value) + const allowed_resources: string[] = resourceInput + ? resourceInput.type === 'static' + ? resourceInput.value + ? [removeResourcePrefix(resourceInput.value)] + : [] + : collectOneOfFields( + { + s3_resource: resourceInput + }, + app + ).s3_resource?.map((s) => removeResourcePrefix(s)) ?? [] + : [] + + const allow_user_resources = + (resourceInput?.type === 'evalv2' && resourceInput?.allowUserResources) ?? false + let file_key_regex = '^.*$' + if (pathTemplateInput) { + if (pathTemplateInput.type === 'static') { + file_key_regex = staticToRegex(pathTemplateInput.value) + } else if ( + pathTemplateInput.type === 'evalv2' && + checkIfEvalIsStringWithFilename(pathTemplateInput) + ) { + file_key_regex = filenameExprToRegex(pathTemplateInput.expr) + } + } + + return { + allow_workspace_resource, + allowed_resources, + allow_user_resources, + file_key_regex + } +} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte index 14ecb4855c..02cfce4ea4 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/InputsSpecEditor.svelte @@ -193,7 +193,7 @@ {:else if componentInput?.type === 'user'} Field's value is set by the user {/if} - {#if (componentInput?.type === 'evalv2' || componentInput?.type === 'connected' || componentInput?.type === 'user') && fieldType == 'object' && format?.startsWith('resource-')} + {#if (componentInput?.type === 'evalv2' || componentInput?.type === 'connected' || componentInput?.type === 'user') && ((fieldType == 'object' && format?.startsWith('resource-') && format !== 'resource-s3_object') || fieldType == 'resource')}
= writable([]) + export let appPath: string | undefined = undefined + export let computeForceViewerPolicies: + | (() => + | { + allowed_resources: string[] + allow_user_resources: boolean + allow_workspace_resource: boolean + file_key_regex: string + } + | undefined) + | undefined = undefined const dispatch = createEventDispatcher() @@ -115,6 +126,26 @@ params.append('content_type', fileToUpload.type) } + if (computeForceViewerPolicies !== undefined) { + const forceViewerPolicies = computeForceViewerPolicies() + + if (forceViewerPolicies) { + params.append( + 'force_viewer_allowed_resources', + forceViewerPolicies.allowed_resources.join(',') + ) + params.append( + 'force_viewer_allow_user_resources', + JSON.stringify(forceViewerPolicies.allow_user_resources) + ) + params.append( + 'force_viewer_allow_workspace_resource', + JSON.stringify(forceViewerPolicies.allow_workspace_resource) + ) + params.append('force_viewer_file_key_regex', forceViewerPolicies.file_key_regex) + } + } + // let response = await fetch( // `/api/w/${$workspaceStore}/job_helpers/multipart_upload_s3_file?${params.toString()}`, // { @@ -158,9 +189,16 @@ } } }) + xhr?.open( 'POST', - `/api/w/${workspace ?? $workspaceStore}/job_helpers/upload_s3_file?${params.toString()}`, + appPath + ? `/api/w/${ + workspace ?? $workspaceStore + }/apps_u/upload_s3_file/${appPath}?${params.toString()}` + : `/api/w/${ + workspace ?? $workspaceStore + }/job_helpers/upload_s3_file?${params.toString()}`, true ) xhr?.setRequestHeader('Content-Type', 'application/octet-stream') @@ -309,7 +347,7 @@ {/if} - {#if fileUpload.progress === 100 && !fileUpload.cancelled} + {#if fileUpload.progress === 100 && !fileUpload.cancelled && $userStore} + {/if} + {:else if id?.startsWith('subflow:')} + + {id.substring('subflow:'.length)} {/if}
diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte index 28167493cf..220b597384 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte @@ -26,7 +26,7 @@ import { getDependentComponents } from '../flowExplorer' import type { FlowCopilotContext } from '$lib/components/copilot/flow' import { fade } from 'svelte/transition' - import { copilotInfo, tutorialsToDo } from '$lib/stores' + import { copilotInfo, tutorialsToDo, workspaceStore } from '$lib/stores' import FlowTutorials from '$lib/components/FlowTutorials.svelte' import { ignoredTutorials } from '$lib/components/tutorials/ignoredTutorials' @@ -44,6 +44,7 @@ export let disableSettings = false export let newFlow: boolean = false export let smallErrorHandler = false + export let workspace: string | undefined = $workspaceStore let flowTutorials: FlowTutorials | undefined = undefined @@ -345,6 +346,7 @@ preprocessorModule={$flowStore.value?.preprocessor_module} {selectedId} {flowInputsStore} + {workspace} on:delete={({ detail }) => { let e = detail.detail dependents = getDependentComponents(e.id, $flowStore) diff --git a/frontend/src/lib/components/flows/map/MapItem.svelte b/frontend/src/lib/components/flows/map/MapItem.svelte index ebd4ffdb9b..05cfc49d90 100644 --- a/frontend/src/lib/components/flows/map/MapItem.svelte +++ b/frontend/src/lib/components/flows/map/MapItem.svelte @@ -20,7 +20,6 @@ export let modules: FlowModule[] export let moving: string | undefined = undefined export let duration_ms: number | undefined = undefined - export let isTrigger: boolean = false export let retries: number | undefined = undefined export let flowJobs: diff --git a/frontend/src/lib/components/flows/map/VirtualItem.svelte b/frontend/src/lib/components/flows/map/VirtualItem.svelte index a2e022e9ef..3f5350b7ed 100644 --- a/frontend/src/lib/components/flows/map/VirtualItem.svelte +++ b/frontend/src/lib/components/flows/map/VirtualItem.svelte @@ -54,7 +54,7 @@
{preLabel}
{/if}
- {#if id && !hideId} + {#if id && !hideId && !id?.startsWith('subflow:')}
{id} diff --git a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte index 47b8ab6032..41f05c1fb5 100644 --- a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte +++ b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte @@ -142,6 +142,7 @@ {alwaysOn} {displayContext} {error} + previousId={pickableProperties?.previousId} {pickableProperties} allowCopy={!notSelectable && !$propPickerConfig} on:select={({ detail }) => { diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index caf28e43ce..d09d22cac7 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -1,5 +1,5 @@ + {#if data.module.value.type == 'flow'} + + {/if} + import VirtualItem from '$lib/components/flows/map/VirtualItem.svelte' + import NodeWrapper from './NodeWrapper.svelte' + import { Minimize2 } from 'lucide-svelte' + import type { GraphModuleState } from '../../model' + import { getStateColor } from '../../util' + import type { FlowModule } from '$lib/gen' + import type { GraphEventHandlers } from '../../graphBuilder' + + export let data: { + label: string + preLabel: string | undefined + insertable: boolean + flowModuleStates: Record | undefined + subflowId: string + id: string + modules: FlowModule[] + selected: boolean + eventHandlers: GraphEventHandlers + offset: number + } + + + + { + data.eventHandlers.select(data.id) + }} + /> + + diff --git a/frontend/src/lib/components/instanceSettings.ts b/frontend/src/lib/components/instanceSettings.ts index 016b5eef78..bd998970e7 100644 --- a/frontend/src/lib/components/instanceSettings.ts +++ b/frontend/src/lib/components/instanceSettings.ts @@ -146,6 +146,15 @@ export const settings: Record = { storage: 'setting', ee_only: 'Channels other than tracing are only available in the EE version' }, + { + label: 'Mute critical alerts in UI', + description: 'Enable to mute critical alerts in the UI', + key: 'critical_alert_mute_ui', + fieldType: 'boolean', + storage: 'setting', + requiresReloadOnChange: true, + ee_only: 'Critical alerts in UI are only available in the EE version' + }, { label: 'Azure OpenAI base path', description: diff --git a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte index b9db54d62b..ffe0269b52 100644 --- a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte +++ b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte @@ -20,6 +20,7 @@ export let allowCopy = true export let collapseLevel: number | undefined = undefined export let prefix = '' + export let expandedEvenOnLevel0: string | undefined = undefined let s3FileViewer: S3FilePicker @@ -124,7 +125,9 @@ {allowCopy} on:select {collapseLevel} - collapsed={collapseLevel !== undefined ? level + 1 >= collapseLevel : undefined} + collapsed={collapseLevel !== undefined + ? level + 1 >= collapseLevel && key != expandedEvenOnLevel0 + : undefined} /> {:else} diff --git a/frontend/src/lib/components/propertyPicker/PropPicker.svelte b/frontend/src/lib/components/propertyPicker/PropPicker.svelte index 62610d48fc..66b85816a3 100644 --- a/frontend/src/lib/components/propertyPicker/PropPicker.svelte +++ b/frontend/src/lib/components/propertyPicker/PropPicker.svelte @@ -11,12 +11,14 @@ import ClearableInput from '../common/clearableInput/ClearableInput.svelte' import { filterNestedObject } from '../flows/previousResults' import type { PropPickerContext } from '../prop_picker' + import Scrollable from '../Scrollable.svelte' export let pickableProperties: PickableProperties export let displayContext = true export let error: boolean = false export let allowCopy = false export let alwaysOn = false + export let previousId: string | undefined = undefined let variables: Record = {} let resources: Record = {} @@ -190,14 +192,22 @@ $: search, $inputMatches, $propPickerConfig, updateState() -
-
+
+
-
+ + @@ -257,6 +267,7 @@ results
@@ -396,5 +408,6 @@
{/if} {/if} -
+ +
diff --git a/frontend/src/lib/components/sidebar/CriticalAlertButton.svelte b/frontend/src/lib/components/sidebar/CriticalAlertButton.svelte new file mode 100644 index 0000000000..9c6d3db68d --- /dev/null +++ b/frontend/src/lib/components/sidebar/CriticalAlertButton.svelte @@ -0,0 +1,64 @@ + + +{#if !disabled} + +
+ +
+ + {#if label} + {label} + {/if} + +
+{/if} diff --git a/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte b/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte new file mode 100644 index 0000000000..68d7bfbffb --- /dev/null +++ b/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte @@ -0,0 +1,72 @@ + + + + + diff --git a/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte b/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte new file mode 100644 index 0000000000..34ff0ffa35 --- /dev/null +++ b/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte @@ -0,0 +1,203 @@ + + +
+ {#if !hasCriticalAlertChannels} +
+ +

+ No critical alert channels are set up. Go to the + Instance Settings + page to configure critical alert channels. +

+
+ {/if} + +
+
+ +
+ + +
+ + +
+
+ + Page {page} + +
+
+ +
+
+ + +
+ + + + + + + + + + + {#each alerts as { id, alert_type, message, created_at, acknowledged }} + {#if !hideAcknowledged || !acknowledged} + + + + + + + + {/if} + {/each} + +
TypeMessageCreated AtAcknowledge
+ {#if alert_type === 'recovered_critical_error'} + + + + {:else} + + + + {/if} + {message}{formatDate(created_at)} +
+ {#if !acknowledged} + + {:else} + + {/if} +
+
+
+ + {#if alerts.length === 0} +

No critical alerts available.

+ {/if} +
diff --git a/frontend/src/lib/components/toast.ts b/frontend/src/lib/components/toast.ts index 8c1afb1a31..f1f1a84f19 100644 --- a/frontend/src/lib/components/toast.ts +++ b/frontend/src/lib/components/toast.ts @@ -1,8 +1,8 @@ //regex that match path starting with u/ or f/ and with at least 2 / const pathRegex = /\b(u|f)\/[^\/\s]+\/[^\/\s]+\b/g -export function processMessage(message: string): string { - return message.replaceAll(pathRegex, (path) => { +export function processMessage(message: string | undefined): string { + return (message ?? 'Error without message').replaceAll(pathRegex, (path) => { return `${path}` }) } diff --git a/frontend/src/lib/components/triggers/RouteEditorInner.svelte b/frontend/src/lib/components/triggers/RouteEditorInner.svelte index 10eaef0725..65fc7db264 100644 --- a/frontend/src/lib/components/triggers/RouteEditorInner.svelte +++ b/frontend/src/lib/components/triggers/RouteEditorInner.svelte @@ -283,7 +283,9 @@ @@ -328,6 +330,7 @@ script_path = '' initialScriptPath = '' is_flow = false + http_method = 'get' } else { static_asset_config = undefined } diff --git a/frontend/src/lib/script_helpers.ts b/frontend/src/lib/script_helpers.ts index 702e60310d..b3f43d2382 100644 --- a/frontend/src/lib/script_helpers.ts +++ b/frontend/src/lib/script_helpers.ts @@ -602,7 +602,7 @@ class Websocket(TypedDict): class WmTrigger(TypedDict): kind: Literal["http", "email", "webhook", "websocket"] http: Http | None - websocket: Websocket | None + websocket: Websocket | None def preprocessor( wm_trigger: WmTrigger, diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts index 18cf32b074..9d8af53bae 100644 --- a/frontend/src/lib/stores.ts +++ b/frontend/src/lib/stores.ts @@ -121,3 +121,5 @@ export type DBSchema = SQLSchema | GraphqlSchema export type DBSchemas = Partial> export const dbSchemas = writable({}) + +export const instanceSettingsSelectedTab = writable('Core') \ No newline at end of file diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index e5b0c3271d..99fc3c0a0c 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -14,7 +14,9 @@ import { classNames, getModifierKey } from '$lib/utils' import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte' import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte' + import CriticalAlertModal from '$lib/components/sidebar/CriticalAlertModal.svelte' import { + enterpriseLicense, copilotInfo, isPremiumStore, starStore, @@ -48,6 +50,7 @@ import MenuButton from '$lib/components/sidebar/MenuButton.svelte' import { setContext } from 'svelte' import { base } from '$app/paths' + import CriticalAlertButton from '$lib/components/sidebar/CriticalAlertButton.svelte' OpenAPI.WITH_CREDENTIALS = true let menuOpen = false @@ -275,6 +278,26 @@ } setContext('openSearchWithPrefilledText', openSearchModal) + + $: { + if ($enterpriseLicense && $superadmin) { + loadCriticalAlertsMuted() + } + } + + let numUnacknowledgedCriticalAlerts = 0 + let isCriticalAlertsModalOpen = false + let isCriticalAlertsUiMuted = false + + async function loadCriticalAlertsMuted() { + isCriticalAlertsUiMuted = (await SettingService.getGlobal({ + key: 'critical_alert_mute_ui' + })) as boolean + } + + function openCriticalAlertsModal(text?: string): void { + isCriticalAlertsModalOpen = true + } @@ -291,6 +314,12 @@ {:else if $userStore} {#if $superadmin} + {#if !isCriticalAlertsUiMuted && $enterpriseLicense} + + {/if} {/if}
@@ -356,7 +385,6 @@ Windmill
-
@@ -405,6 +433,17 @@ {/if}
+ {#if $superadmin && $enterpriseLicense} + openCriticalAlertsModal()} + {numUnacknowledgedCriticalAlerts} + {isCollapsed} + label="Critical Alerts" + class="!text-xs" + disabled={numUnacknowledgedCriticalAlerts === 0} + /> + {/if}
diff --git a/frontend/src/routes/(root)/(logged)/routes/+page.svelte b/frontend/src/routes/(root)/(logged)/routes/+page.svelte index c2f12fbe8a..8906031338 100644 --- a/frontend/src/routes/(root)/(logged)/routes/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/routes/+page.svelte @@ -200,7 +200,7 @@
No routes
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, route_path, is_flow, extra_perms, canWrite, marked, http_method } (path)} + {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, route_path, is_flow, extra_perms, canWrite, marked, http_method, static_asset_config } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`}
- runnable: {script_path} + {#if static_asset_config} + file: {static_asset_config.s3} + {:else} + runnable: {script_path} + {/if}
diff --git a/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte b/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte index 2175c75009..eedeca636c 100644 --- a/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte +++ b/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte @@ -284,6 +284,7 @@

Flow details