Compare commits
15 Commits
v1.631.0
...
rf/testbac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9bac09b14 | ||
|
|
607a861b80 | ||
|
|
5ecfa92ac3 | ||
|
|
a3d5ae5425 | ||
|
|
d363fa6098 | ||
|
|
44143e7576 | ||
|
|
4d94d80835 | ||
|
|
c9d5c24c34 | ||
|
|
b11d6ed794 | ||
|
|
ea52a8b8ce | ||
|
|
b9e0ccaefd | ||
|
|
5effb87a36 | ||
|
|
900c76ccad | ||
|
|
ff72b81004 | ||
|
|
2815cfae1a |
16
.github/workflows/backend-test.yml
vendored
16
.github/workflows/backend-test.yml
vendored
@@ -19,7 +19,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
cargo_test:
|
||||
runs-on: ubicloud-standard-16
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
@@ -50,9 +50,9 @@ jobs:
|
||||
- uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.21.5
|
||||
go-version: 1.25.0
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.8
|
||||
@@ -72,11 +72,13 @@ jobs:
|
||||
bundler-cache: false
|
||||
- name: Install PowerShell, mold and clang
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y powershell mold clang libcurl4-openssl-dev
|
||||
dotnet tool install --global PowerShell
|
||||
echo "$HOME/.dotnet/tools" >> $GITHUB_PATH
|
||||
sudo apt-get update && sudo apt-get install -y mold clang libcurl4-openssl-dev
|
||||
working-directory: /
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false
|
||||
cache: true
|
||||
toolchain: 1.93.0
|
||||
- name: Read EE repo commit hash
|
||||
run: |
|
||||
@@ -214,7 +216,7 @@ jobs:
|
||||
RUST_LOG: "off"
|
||||
RUST_LOG_STYLE: never
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
CARGO_BUILD_JOBS: 12
|
||||
CARGO_BUILD_JOBS: 16
|
||||
WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1
|
||||
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
|
||||
WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1
|
||||
@@ -222,4 +224,4 @@ jobs:
|
||||
run: |
|
||||
deno --version && bun -v && node --version && go version && python3 --version && php --version && ruby --version && pwsh --version && dotnet --version
|
||||
cd windmill-duckdb-ffi-internal && ./build_dev.sh && cd ..
|
||||
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) PHP_PATH=$(which php) COMPOSER_PATH=$(which composer) RUBY_PATH=$(which ruby) RUBY_BUNDLE_PATH=$(which bundle) RUBY_GEM_PATH=$(which gem) POWERSHELL_PATH=$(which pwsh) DOTNET_PATH=$(which dotnet) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test,csharp,php,ruby,mysql,quickjs,mcp --all -- --nocapture --test-threads=10
|
||||
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) PHP_PATH=$(which php) COMPOSER_PATH=$(which composer) RUBY_PATH=$(which ruby) RUBY_BUNDLE_PATH=$(which bundle) RUBY_GEM_PATH=$(which gem) POWERSHELL_PATH=$(which pwsh) DOTNET_PATH=$(which dotnet) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test,csharp,php,ruby,mysql,quickjs,mcp --all -- --nocapture --test-threads=12
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## [1.631.2](https://github.com/windmill-labs/windmill/compare/v1.631.1...v1.631.2) (2026-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** revert CloseButton refactor that broke tag removal in MultiSelect ([#7909](https://github.com/windmill-labs/windmill/issues/7909)) ([b11d6ed](https://github.com/windmill-labs/windmill/commit/b11d6ed7940faddfe74a22b25bcb132527cbcec8))
|
||||
* nix flake libz.so for deno_core ([#7905](https://github.com/windmill-labs/windmill/issues/7905)) ([900c76c](https://github.com/windmill-labs/windmill/commit/900c76ccad09f58fc6adcfa8151db780249617f6))
|
||||
* strip unsupported schema fields for Google AI ([#7894](https://github.com/windmill-labs/windmill/issues/7894)) ([5effb87](https://github.com/windmill-labs/windmill/commit/5effb87a36793e20d17e678619091ef458fe8f0a)), closes [#7759](https://github.com/windmill-labs/windmill/issues/7759)
|
||||
|
||||
## [1.631.1](https://github.com/windmill-labs/windmill/compare/v1.631.0...v1.631.1) (2026-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add kafka-gssapi support to ee builds ([2815cfa](https://github.com/windmill-labs/windmill/commit/2815cfae1a5eefb2c553e893dc926e62ad1df528))
|
||||
|
||||
## [1.631.0](https://github.com/windmill-labs/windmill/compare/v1.630.2...v1.631.0) (2026-02-11)
|
||||
|
||||
|
||||
|
||||
@@ -49,6 +49,10 @@ Windmill uses a workspace-based architecture with multiple crates:
|
||||
- Use feature flags: `#[cfg(feature = "enterprise")]`
|
||||
- Isolate enterprise code in separate modules
|
||||
|
||||
## Git Workflow
|
||||
|
||||
- **Never push directly to main** — always create a branch and open a pull request
|
||||
|
||||
## Testing
|
||||
|
||||
- Write unit tests for core functionality
|
||||
|
||||
211
backend/Cargo.lock
generated
211
backend/Cargo.lock
generated
@@ -2322,9 +2322,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.57"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a"
|
||||
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -2332,9 +2332,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.57"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238"
|
||||
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -2356,9 +2356,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.7"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
@@ -15725,7 +15725,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -15778,6 +15778,7 @@ dependencies = [
|
||||
"windmill-indexer",
|
||||
"windmill-queue",
|
||||
"windmill-runtime-nativets",
|
||||
"windmill-test-utils",
|
||||
"windmill-worker",
|
||||
"windows-service",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -15785,7 +15786,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-alerting"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15798,7 +15799,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -15890,6 +15891,7 @@ dependencies = [
|
||||
"windmill-api-debug",
|
||||
"windmill-api-embeddings",
|
||||
"windmill-api-flow-conversations",
|
||||
"windmill-api-flows",
|
||||
"windmill-api-groups",
|
||||
"windmill-api-inputs",
|
||||
"windmill-api-jobs",
|
||||
@@ -15934,7 +15936,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-agent-workers"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15957,7 +15959,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-assets"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15970,7 +15972,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-auth"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -15996,7 +15998,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
@@ -16006,7 +16008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-configs"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16023,7 +16025,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-debug"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
@@ -16046,7 +16048,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-embeddings"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16069,7 +16071,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flow-conversations"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16083,9 +16085,29 @@ dependencies = [
|
||||
"windmill-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flows"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
"hyper 1.8.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sql-builder",
|
||||
"sqlx",
|
||||
"tracing",
|
||||
"windmill-api-auth",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
"windmill-dep-map",
|
||||
"windmill-git-sync",
|
||||
"windmill-queue",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-groups"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16105,7 +16127,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-inputs"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16117,9 +16139,33 @@ dependencies = [
|
||||
"windmill-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-integration-tests"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
"aws-config",
|
||||
"aws-credential-types",
|
||||
"aws-sdk-sqs",
|
||||
"base64 0.22.1",
|
||||
"rand 0.9.0",
|
||||
"rdkafka",
|
||||
"reqwest 0.13.1",
|
||||
"rumqttc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"uuid",
|
||||
"windmill-api-client",
|
||||
"windmill-common",
|
||||
"windmill-test-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-jobs"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16144,7 +16190,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-npm-proxy"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"flate2",
|
||||
@@ -16160,7 +16206,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-openapi"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16181,7 +16227,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-schedule"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16201,7 +16247,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-scripts"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16230,7 +16276,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-settings"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16256,7 +16302,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-sse"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
@@ -16268,7 +16314,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-users"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum 0.7.9",
|
||||
@@ -16291,7 +16337,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workers"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16305,7 +16351,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workspaces"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16334,7 +16380,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
@@ -16348,7 +16394,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16367,7 +16413,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
@@ -16466,7 +16512,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-dep-map"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"itertools 0.14.0",
|
||||
@@ -16485,7 +16531,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -16500,7 +16546,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"astral-tokio-tar",
|
||||
@@ -16524,7 +16570,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-jseval"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16541,7 +16587,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
@@ -16557,7 +16603,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-mcp"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16578,7 +16624,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-native-triggers"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16609,7 +16655,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-oauth"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-oauth2",
|
||||
@@ -16633,7 +16679,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -16642,7 +16688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16654,7 +16700,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16666,7 +16712,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -16678,7 +16724,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16690,7 +16736,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16702,7 +16748,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -16713,7 +16759,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16724,7 +16770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16737,7 +16783,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16761,7 +16807,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16775,7 +16821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -16792,7 +16838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16806,7 +16852,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16825,7 +16871,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -16836,7 +16882,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16873,7 +16919,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-runtime-nativets"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"const_format",
|
||||
@@ -16911,7 +16957,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
@@ -16921,7 +16967,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-store"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16948,9 +16994,32 @@ dependencies = [
|
||||
"windmill-oauth",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-test-utils"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
"futures",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"windmill-api",
|
||||
"windmill-api-agent-workers",
|
||||
"windmill-api-client",
|
||||
"windmill-common",
|
||||
"windmill-queue",
|
||||
"windmill-runtime-nativets",
|
||||
"windmill-worker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16983,7 +17052,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-email"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17003,7 +17072,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-gcp"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17037,7 +17106,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-http"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17071,7 +17140,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-kafka"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17094,7 +17163,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-mqtt"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17118,7 +17187,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-nats"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -17142,7 +17211,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-postgres"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17177,7 +17246,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-sqs"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17205,7 +17274,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-websocket"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17228,7 +17297,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-types"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -17245,7 +17314,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-once-cell",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -9,6 +9,7 @@ resolver = "2"
|
||||
members = [
|
||||
"./windmill-api",
|
||||
"./windmill-api-scripts",
|
||||
"./windmill-api-flows",
|
||||
"./windmill-api-users",
|
||||
"./windmill-api-workspaces",
|
||||
"./windmill-api-groups",
|
||||
@@ -66,12 +67,14 @@ members = [
|
||||
"./parsers/windmill-parser-py",
|
||||
"./parsers/windmill-parser-py-imports",
|
||||
"./parsers/windmill-sql-datatype-parser-wasm",
|
||||
"./parsers/windmill-parser-yaml", "windmill-macros", "parsers/windmill-parser-nu"
|
||||
"./parsers/windmill-parser-yaml", "windmill-macros", "parsers/windmill-parser-nu",
|
||||
"./windmill-test-utils",
|
||||
"./windmill-api-integration-tests",
|
||||
]
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -172,8 +175,8 @@ oss = ["oss_core", "all_languages", "no_auth"]
|
||||
ce_rpi = ["ce_core", "all_languages"]
|
||||
ce = ["ce_rpi", "jemalloc", "dind", "agent_worker_server"]
|
||||
# Edition meta-features: EE variants
|
||||
ee = ["ce", "ee_core", "ee_server"]
|
||||
ee_rhel = ["ce_core", "ee_core", "all_languages"]
|
||||
ee = ["ce", "ee_core", "ee_server", "kafka-gssapi"]
|
||||
ee_rhel = ["ce_core", "ee_core", "kafka-gssapi", "all_languages"]
|
||||
ee_windows = ["ce_core", "ee_core", "all_languages_windows"]
|
||||
all_sqlx_features = ["all_languages", "enterprise", "enterprise_saml", "embedding", "parquet", "prometheus", "flow_testing",
|
||||
"openidconnect", "cloud", "jemalloc", "tantivy", "sqlx", "kafka", "kafka-gssapi", "nats", "otel", "dind", "websocket", "http_trigger",
|
||||
@@ -239,6 +242,7 @@ serde_json.workspace = true
|
||||
reqwest.workspace = true
|
||||
windmill-queue = { workspace = true, features = ["failpoints"] }
|
||||
windmill-dep-map.workspace = true
|
||||
windmill-test-utils.workspace = true
|
||||
axum.workspace = true
|
||||
serde.workspace = true
|
||||
windmill-api-client.workspace = true
|
||||
@@ -267,6 +271,7 @@ windmill-oauth = {path = "./windmill-oauth"}
|
||||
windmill-macros = {path = "./windmill-macros"}
|
||||
windmill-api-auth = { path = "./windmill-api-auth" }
|
||||
windmill-api-scripts = { path = "./windmill-api-scripts" }
|
||||
windmill-api-flows = { path = "./windmill-api-flows" }
|
||||
windmill-api-users = { path = "./windmill-api-users" }
|
||||
windmill-api-workspaces = { path = "./windmill-api-workspaces" }
|
||||
windmill-api-groups = { path = "./windmill-api-groups" }
|
||||
@@ -315,6 +320,7 @@ windmill-parser-php = { path = "./parsers/windmill-parser-php" }
|
||||
windmill-jseval = { path = "./windmill-jseval" }
|
||||
windmill-runtime-nativets = { path = "./windmill-runtime-nativets" }
|
||||
windmill-api-client = { path = "./windmill-api-client" }
|
||||
windmill-test-utils = { path = "./windmill-test-utils" }
|
||||
|
||||
reqwest-retry = "^0"
|
||||
reqwest-middleware = { version = "^0", features = ["json"] }
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#![cfg(all(feature = "private", feature = "agent_worker_server"))]
|
||||
|
||||
mod common;
|
||||
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
mod common;
|
||||
use crate::common::*;
|
||||
use windmill_test_utils::*;
|
||||
use sqlx::postgres::Postgres;
|
||||
use sqlx::Pool;
|
||||
use windmill_common::jobs::{JobPayload, RawCode};
|
||||
@@ -860,7 +859,7 @@ export function main() {
|
||||
// ============================================================================
|
||||
|
||||
mod dedicated_worker_protocol {
|
||||
use crate::common::{parse_dedicated_worker_line, DedicatedWorkerResult};
|
||||
use windmill_test_utils::{parse_dedicated_worker_line, DedicatedWorkerResult};
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::process::{Command, Stdio};
|
||||
use windmill_worker::{
|
||||
|
||||
@@ -2,8 +2,9 @@ use sqlx::{Pool, Postgres};
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
use windmill_api_client::types::NewScript;
|
||||
mod common;
|
||||
use common::{in_test_worker, init_client, listen_for_completed_jobs, ApiServer};
|
||||
use windmill_test_utils::{
|
||||
in_test_worker, init_client, listen_for_completed_jobs, rebuild_dmap, ApiServer,
|
||||
};
|
||||
|
||||
mod dependency_map {
|
||||
use super::*;
|
||||
@@ -170,7 +171,7 @@ mod dependency_map {
|
||||
let (client, _port, _s) = init(db.clone()).await;
|
||||
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
|
||||
// rebuild map
|
||||
assert!(common::rebuild_dmap(&client).await);
|
||||
assert!(rebuild_dmap(&client).await);
|
||||
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
|
||||
Ok(())
|
||||
}
|
||||
@@ -183,7 +184,7 @@ mod dependency_map {
|
||||
// Spawn first rebuild
|
||||
let handle = {
|
||||
let client = client.clone();
|
||||
tokio::spawn(async move { common::rebuild_dmap(&client).await })
|
||||
tokio::spawn(async move { rebuild_dmap(&client).await })
|
||||
};
|
||||
|
||||
// Immidiately spawn another
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
/// Test that workspace error handler can be set and removed via database operations
|
||||
#[cfg(feature = "deno_core")]
|
||||
|
||||
@@ -25,8 +25,7 @@ use windmill_common::{
|
||||
scripts::ScriptLang,
|
||||
};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
/// Helper to create a FlowModule with default fields
|
||||
fn flow_module(id: &str, value: FlowModuleValue) -> FlowModule {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
mod common;
|
||||
|
||||
mod job_payload {
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
@@ -8,7 +6,7 @@ mod job_payload {
|
||||
use windmill_common::scripts::{ScriptHash, ScriptLang};
|
||||
use windmill_common::flow_status::RestartedFrom;
|
||||
|
||||
use crate::common::*;
|
||||
use windmill_test_utils::*;
|
||||
use windmill_common::min_version::{
|
||||
MIN_VERSION, MIN_VERSION_IS_AT_LEAST_1_427, MIN_VERSION_IS_AT_LEAST_1_432,
|
||||
MIN_VERSION_IS_AT_LEAST_1_440,
|
||||
|
||||
@@ -7,8 +7,7 @@ use windmill_common::{
|
||||
scripts::ScriptLang,
|
||||
};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ListJobsResponse {
|
||||
|
||||
@@ -14,10 +14,7 @@
|
||||
*/
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
mod common;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use futures::StreamExt;
|
||||
|
||||
@@ -11,10 +11,7 @@
|
||||
*/
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
mod common;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
mod common;
|
||||
use crate::common::*;
|
||||
use windmill_test_utils::*;
|
||||
use sqlx::postgres::Postgres;
|
||||
use sqlx::Pool;
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
mod common;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
mod retry {
|
||||
use crate::common::*;
|
||||
use windmill_test_utils::*;
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
/// Test that the workspace success handler cache works correctly with 60s TTL
|
||||
#[cfg(feature = "deno_core")]
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
mod common;
|
||||
|
||||
mod suspend_resume {
|
||||
#[cfg(feature = "deno_core")]
|
||||
use serde_json::json;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use crate::common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use futures::{Stream, StreamExt};
|
||||
|
||||
@@ -25,8 +25,7 @@ use windmill_common::{
|
||||
jobs::{JobPayload, RawCode},
|
||||
scripts::ScriptLang,
|
||||
};
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use futures::StreamExt;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
mod common;
|
||||
mod workspace_dependencies {
|
||||
|
||||
use crate::common::in_test_worker;
|
||||
use crate::common::init_client;
|
||||
use crate::common::listen_for_completed_jobs;
|
||||
use windmill_test_utils::in_test_worker;
|
||||
use windmill_test_utils::init_client;
|
||||
use windmill_test_utils::listen_for_completed_jobs;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tokio_stream::StreamExt;
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
|
||||
31
backend/windmill-api-flows/Cargo.toml
Normal file
31
backend/windmill-api-flows/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "windmill-api-flows"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_api_flows"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
enterprise = ["windmill-common/enterprise"]
|
||||
private = ["windmill-common/private"]
|
||||
|
||||
[dependencies]
|
||||
windmill-common = { workspace = true, default-features = false }
|
||||
windmill-api-auth.workspace = true
|
||||
windmill-queue.workspace = true
|
||||
windmill-audit.workspace = true
|
||||
windmill-git-sync.workspace = true
|
||||
windmill-dep-map.workspace = true
|
||||
|
||||
axum.workspace = true
|
||||
hyper.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sql-builder.workspace = true
|
||||
sqlx.workspace = true
|
||||
tracing.workspace = true
|
||||
chrono.workspace = true
|
||||
9
backend/windmill-api-flows/src/lib.rs
Normal file
9
backend/windmill-api-flows/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Author: Ruben Fiszel
|
||||
* Copyright: Windmill Labs, Inc 2022
|
||||
* This file and its contents are licensed under the AGPLv3 License.
|
||||
* Please see the included NOTICE for copyright information and
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
pub mod flows;
|
||||
36
backend/windmill-api-integration-tests/Cargo.toml
Normal file
36
backend/windmill-api-integration-tests/Cargo.toml
Normal file
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "windmill-api-integration-tests"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_api_integration_tests"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
private = ["windmill-test-utils/private", "dep:aws-config", "dep:aws-credential-types", "dep:aws-sdk-sqs"]
|
||||
enterprise = ["windmill-test-utils/enterprise", "dep:base64"]
|
||||
deno_core = ["windmill-test-utils/deno_core"]
|
||||
mcp = []
|
||||
|
||||
[dependencies]
|
||||
windmill-test-utils.workspace = true
|
||||
windmill-api-client.workspace = true
|
||||
windmill-common = { workspace = true, default-features = false }
|
||||
sqlx.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
reqwest.workspace = true
|
||||
tokio.workspace = true
|
||||
anyhow.workspace = true
|
||||
uuid.workspace = true
|
||||
rand.workspace = true
|
||||
rumqttc.workspace = true
|
||||
rdkafka.workspace = true
|
||||
async-nats.workspace = true
|
||||
aws-config = { workspace = true, optional = true }
|
||||
aws-credential-types = { workspace = true, optional = true }
|
||||
aws-sdk-sqs = { workspace = true, optional = true }
|
||||
base64 = { workspace = true, optional = true }
|
||||
2
backend/windmill-api-integration-tests/src/lib.rs
Normal file
2
backend/windmill-api-integration-tests/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// This crate exists only to host integration tests.
|
||||
// See tests/ directory for the actual test files.
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn app_url(port: u16, endpoint: &str, path: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/apps/{endpoint}/{path}")
|
||||
@@ -40,7 +39,7 @@ fn new_app(path: &str, summary: &str) -> serde_json::Value {
|
||||
})
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_app_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn client() -> reqwest::Client {
|
||||
reqwest::Client::new()
|
||||
@@ -12,7 +11,7 @@ fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
builder.header("Authorization", "Bearer SECRET_TOKEN")
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_draft_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
146
backend/windmill-api-integration-tests/tests/fixtures/base.sql
vendored
Normal file
146
backend/windmill-api-integration-tests/tests/fixtures/base.sql
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
-- used for backend automated testing
|
||||
-- https://docs.rs/sqlx/latest/sqlx/attr.test.html
|
||||
|
||||
INSERT INTO workspace
|
||||
(id, name, owner)
|
||||
VALUES ('test-workspace', 'test-workspace', 'test-user');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test@windmill.dev', 'test-user', true, 'Admin');
|
||||
|
||||
INSERT INTO workspace_key(workspace_id, kind, key) VALUES
|
||||
('test-workspace', 'cloud', 'test-key');
|
||||
|
||||
|
||||
INSERT INTO workspace_settings (workspace_id) VALUES
|
||||
('test-workspace');
|
||||
|
||||
INSERT INTO group_ (workspace_id, name, summary, extra_perms) VALUES
|
||||
('test-workspace', 'all', 'All users', '{}');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name, username)
|
||||
VALUES ('test@windmill.dev', 'not-a-real-hash', 'password', true, true, 'Test User', 'test-user');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES ('test2@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 2');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES ('test3@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 3');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test2@windmill.dev', 'test-user-2', false, 'User');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test3@windmill.dev', 'test-user-3', false, 'User');
|
||||
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN', 'test@windmill.dev', 'test token', true);
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN_2', 'test2@windmill.dev', 'test token 2', false);
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN_3', 'test3@windmill.dev', 'test token 3', false);
|
||||
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_admin;
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_user;
|
||||
|
||||
CREATE FUNCTION "notify_insert_on_completed_job" ()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('completed', NEW.id::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER "notify_insert_on_completed_job"
|
||||
AFTER INSERT ON "v2_job_completed"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION "notify_insert_on_completed_job" ();
|
||||
|
||||
|
||||
CREATE FUNCTION "notify_queue" ()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('queued', NEW.id::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER "notify_queue_after_insert"
|
||||
AFTER INSERT ON "v2_job_queue"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION "notify_queue" ();
|
||||
|
||||
CREATE TRIGGER "notify_queue_after_flow_status_update"
|
||||
AFTER UPDATE ON "v2_job_status"
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.flow_status IS DISTINCT FROM OLD.flow_status)
|
||||
EXECUTE FUNCTION "notify_queue" ();
|
||||
|
||||
-- Apply phase 4:
|
||||
DROP FUNCTION IF EXISTS v2_job_after_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_completed_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_completed_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_after_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_runtime_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_runtime_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_status_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_status_before_update CASCADE;
|
||||
|
||||
DROP VIEW IF EXISTS completed_job, completed_job_view, job, queue, queue_view CASCADE;
|
||||
|
||||
ALTER TABLE v2_job_queue
|
||||
DROP COLUMN IF EXISTS __parent_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_by CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_hash CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __args CASCADE,
|
||||
DROP COLUMN IF EXISTS __logs CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_code CASCADE,
|
||||
DROP COLUMN IF EXISTS __canceled CASCADE,
|
||||
DROP COLUMN IF EXISTS __last_ping CASCADE,
|
||||
DROP COLUMN IF EXISTS __job_kind CASCADE,
|
||||
DROP COLUMN IF EXISTS __env_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __schedule_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __permissioned_as CASCADE,
|
||||
DROP COLUMN IF EXISTS __flow_status CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_flow CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_flow_step CASCADE,
|
||||
DROP COLUMN IF EXISTS __language CASCADE,
|
||||
DROP COLUMN IF EXISTS __same_worker CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_lock CASCADE,
|
||||
DROP COLUMN IF EXISTS __pre_run_error CASCADE,
|
||||
DROP COLUMN IF EXISTS __email CASCADE,
|
||||
DROP COLUMN IF EXISTS __visible_to_owner CASCADE,
|
||||
DROP COLUMN IF EXISTS __mem_peak CASCADE,
|
||||
DROP COLUMN IF EXISTS __root_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __leaf_jobs CASCADE,
|
||||
DROP COLUMN IF EXISTS __concurrent_limit CASCADE,
|
||||
DROP COLUMN IF EXISTS __concurrency_time_window_s CASCADE,
|
||||
DROP COLUMN IF EXISTS __timeout CASCADE,
|
||||
DROP COLUMN IF EXISTS __flow_step_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __cache_ttl CASCADE;
|
||||
|
||||
LOCK TABLE v2_job_queue IN ACCESS EXCLUSIVE MODE;
|
||||
ALTER TABLE v2_job_completed
|
||||
DROP COLUMN IF EXISTS __parent_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_by CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_at CASCADE,
|
||||
DROP COLUMN IF EXISTS __success CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_hash CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __args CASCADE,
|
||||
DROP COLUMN IF EXISTS __logs CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_code CASCADE,
|
||||
DROP COLUMN IF EXISTS __canceled CASCADE,
|
||||
DROP COLUMN IF EXISTS __job_kind CASCADE,
|
||||
DROP COLUMN IF EXISTS __env_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __schedule_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __permissioned_as CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_flow CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_flow_step CASCADE,
|
||||
DROP COLUMN IF EXISTS __language CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_skipped CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_lock CASCADE,
|
||||
DROP COLUMN IF EXISTS __email CASCADE,
|
||||
DROP COLUMN IF EXISTS __visible_to_owner CASCADE,
|
||||
DROP COLUMN IF EXISTS __tag CASCADE,
|
||||
DROP COLUMN IF EXISTS __priority CASCADE;
|
||||
238
backend/windmill-api-integration-tests/tests/fixtures/permissions_test.sql
vendored
Normal file
238
backend/windmill-api-integration-tests/tests/fixtures/permissions_test.sql
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
-- Permissions test fixture
|
||||
-- This sets up a comprehensive permission testing scenario
|
||||
|
||||
-- ============================================
|
||||
-- USERS
|
||||
-- ============================================
|
||||
|
||||
-- Admin user (may already exist from base.sql, use ON CONFLICT)
|
||||
INSERT INTO usr (workspace_id, email, username, is_admin, role)
|
||||
VALUES ('test-workspace', 'admin@windmill.dev', 'admin', true, 'Admin')
|
||||
ON CONFLICT (workspace_id, username) DO NOTHING;
|
||||
|
||||
-- Regular users (non-admin)
|
||||
INSERT INTO usr (workspace_id, email, username, is_admin, role)
|
||||
VALUES
|
||||
('test-workspace', 'alice@windmill.dev', 'alice', false, 'Developer'),
|
||||
('test-workspace', 'bob@windmill.dev', 'bob', false, 'Developer'),
|
||||
('test-workspace', 'charlie@windmill.dev', 'charlie', false, 'Developer');
|
||||
|
||||
-- Operator user (can execute but cannot create/update scripts, flows, apps)
|
||||
INSERT INTO usr (workspace_id, email, username, is_admin, operator, role)
|
||||
VALUES
|
||||
('test-workspace', 'operator@windmill.dev', 'operator', false, true, 'Operator');
|
||||
|
||||
-- Add users to password table (use ON CONFLICT since admin may exist from base.sql)
|
||||
INSERT INTO password (email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES
|
||||
('admin@windmill.dev', 'dummy_hash', 'password', false, true, 'Admin User'),
|
||||
('alice@windmill.dev', 'dummy_hash', 'password', false, true, 'Alice'),
|
||||
('bob@windmill.dev', 'dummy_hash', 'password', false, true, 'Bob'),
|
||||
('charlie@windmill.dev', 'dummy_hash', 'password', false, true, 'Charlie'),
|
||||
('operator@windmill.dev', 'dummy_hash', 'password', false, true, 'Operator')
|
||||
ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
-- ============================================
|
||||
-- TOKENS for authentication
|
||||
-- ============================================
|
||||
|
||||
-- Tokens associated with emails (workspace-scoped)
|
||||
-- The auth system will look up the user by email in the usr table
|
||||
-- Note: tokens must be at least 10 characters (TOKEN_PREFIX_LEN)
|
||||
INSERT INTO token (token, email, label, super_admin, owner, workspace_id)
|
||||
VALUES
|
||||
('ADMIN_TOKEN_TEST', 'admin@windmill.dev', 'Admin token', false, 'u/admin', 'test-workspace'),
|
||||
('ALICE_TOKEN_TEST', 'alice@windmill.dev', 'Alice token', false, 'u/alice', 'test-workspace'),
|
||||
('BOB_TOKEN_TEST12', 'bob@windmill.dev', 'Bob token', false, 'u/bob', 'test-workspace'),
|
||||
('CHARLIE_TOKEN_01', 'charlie@windmill.dev', 'Charlie token', false, 'u/charlie', 'test-workspace'),
|
||||
('OPERATOR_TOKEN_1', 'operator@windmill.dev', 'Operator token', false, 'u/operator', 'test-workspace');
|
||||
|
||||
-- ============================================
|
||||
-- GROUPS
|
||||
-- ============================================
|
||||
|
||||
-- 'developers' group - Charlie is a member
|
||||
INSERT INTO group_ (workspace_id, name, summary, extra_perms)
|
||||
VALUES ('test-workspace', 'developers', 'Developer group', '{}');
|
||||
|
||||
-- 'editors' group - Bob is a member (has write access to some folders)
|
||||
INSERT INTO group_ (workspace_id, name, summary, extra_perms)
|
||||
VALUES ('test-workspace', 'editors', 'Editor group', '{}');
|
||||
|
||||
-- Group memberships
|
||||
INSERT INTO usr_to_group (workspace_id, group_, usr)
|
||||
VALUES
|
||||
('test-workspace', 'developers', 'charlie'),
|
||||
('test-workspace', 'editors', 'bob');
|
||||
|
||||
-- ============================================
|
||||
-- FOLDERS
|
||||
-- ============================================
|
||||
|
||||
-- 'shared' folder:
|
||||
-- - Alice has read-only access (extra_perms: u/alice -> false)
|
||||
-- - Bob has write access (extra_perms: u/bob -> true)
|
||||
INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'shared', 'Shared Folder', '{"u/admin"}',
|
||||
'{"u/alice": false, "u/bob": true}', 'admin');
|
||||
|
||||
-- 'team' folder:
|
||||
-- - 'developers' group has read-only access
|
||||
INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'team', 'Team Folder', '{"u/admin"}',
|
||||
'{"g/developers": false}', 'admin');
|
||||
|
||||
-- 'editable' folder:
|
||||
-- - 'editors' group has write access
|
||||
INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'editable', 'Editable Folder', '{"u/admin"}',
|
||||
'{"g/editors": true}', 'admin');
|
||||
|
||||
-- 'admin_only' folder:
|
||||
-- - Only admin has access (but we'll add item-level perms for specific items)
|
||||
INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'admin_only', 'Admin Only Folder', '{"u/admin"}',
|
||||
'{}', 'admin');
|
||||
|
||||
-- 'alice_owned' folder:
|
||||
-- - Alice is an owner (owners must also be in extra_perms for get_folders_for_user to find them)
|
||||
INSERT INTO folder (workspace_id, name, display_name, owners, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'alice_owned', 'Alice Owned Folder', '{"u/alice"}',
|
||||
'{"u/alice": true}', 'alice');
|
||||
|
||||
-- ============================================
|
||||
-- SCRIPTS
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal script
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1001, 'u/alice/my_script',
|
||||
'export function main() { return "alice script"; }',
|
||||
'deno', 'script', 'alice', '{}', 'Alice script', '', '', '{}');
|
||||
|
||||
-- Bob's personal script
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1002, 'u/bob/my_script',
|
||||
'export function main() { return "bob script"; }',
|
||||
'deno', 'script', 'bob', '{}', 'Bob script', '', '', '{}');
|
||||
|
||||
-- Script in shared folder (accessible by Alice read-only, Bob write)
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1003, 'f/shared/public_script',
|
||||
'export function main() { return "public"; }',
|
||||
'deno', 'script', 'admin', '{}', 'Public script', '', '', '{}');
|
||||
|
||||
-- Script in team folder (accessible by developers group)
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1004, 'f/team/team_script',
|
||||
'export function main() { return "team"; }',
|
||||
'deno', 'script', 'admin', '{}', 'Team script', '', '', '{}');
|
||||
|
||||
-- Script in admin_only folder but with explicit share to Alice
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1005, 'f/admin_only/shared_with_alice',
|
||||
'export function main() { return "shared"; }',
|
||||
'deno', 'script', 'admin', '{}', 'Shared with Alice', '', '',
|
||||
'{"u/alice": false}');
|
||||
|
||||
-- Script in alice_owned folder
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1006, 'f/alice_owned/owner_script',
|
||||
'export function main() { return "owner"; }',
|
||||
'deno', 'script', 'alice', '{}', 'Owner script', '', '', '{}');
|
||||
|
||||
-- Alice's script with extra_perms sharing to Bob (read-only)
|
||||
INSERT INTO script (workspace_id, hash, path, content, language, kind, created_by, schema, summary, description, lock, extra_perms)
|
||||
VALUES ('test-workspace', 1007, 'u/alice/extra_shared_script',
|
||||
'export function main() { return "extra shared"; }',
|
||||
'deno', 'script', 'alice', '{}', 'Extra shared script', '', '',
|
||||
'{"u/bob": false}');
|
||||
|
||||
-- ============================================
|
||||
-- FLOWS (with flow_versions)
|
||||
-- ============================================
|
||||
|
||||
-- Create flows first (without versions)
|
||||
INSERT INTO flow (workspace_id, path, summary, description, value, edited_by, edited_at, schema, extra_perms)
|
||||
VALUES
|
||||
('test-workspace', 'u/alice/my_flow', 'Alice flow', '', '{"modules": []}', 'alice', NOW(), '{}', '{}'),
|
||||
('test-workspace', 'u/bob/my_flow', 'Bob flow', '', '{"modules": []}', 'bob', NOW(), '{}', '{}'),
|
||||
('test-workspace', 'f/shared/shared_flow', 'Shared flow', '', '{"modules": []}', 'admin', NOW(), '{}', '{}');
|
||||
|
||||
-- Create flow versions
|
||||
INSERT INTO flow_version (id, workspace_id, path, value, schema, created_by, created_at)
|
||||
VALUES
|
||||
(1001, 'test-workspace', 'u/alice/my_flow', '{"modules": []}', '{}', 'alice', NOW()),
|
||||
(1002, 'test-workspace', 'u/bob/my_flow', '{"modules": []}', '{}', 'bob', NOW()),
|
||||
(1003, 'test-workspace', 'f/shared/shared_flow', '{"modules": []}', '{}', 'admin', NOW());
|
||||
|
||||
-- Update flows with version references
|
||||
UPDATE flow SET versions = ARRAY[1001::bigint] WHERE path = 'u/alice/my_flow' AND workspace_id = 'test-workspace';
|
||||
UPDATE flow SET versions = ARRAY[1002::bigint] WHERE path = 'u/bob/my_flow' AND workspace_id = 'test-workspace';
|
||||
UPDATE flow SET versions = ARRAY[1003::bigint] WHERE path = 'f/shared/shared_flow' AND workspace_id = 'test-workspace';
|
||||
|
||||
-- ============================================
|
||||
-- RESOURCES
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal resource
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/alice/my_resource', '{"key": "alice_value"}',
|
||||
'Alice resource', 'object', '{}', 'alice');
|
||||
|
||||
-- Bob's personal resource
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/bob/my_resource', '{"key": "bob_value"}',
|
||||
'Bob resource', 'object', '{}', 'bob');
|
||||
|
||||
-- ============================================
|
||||
-- VARIABLES
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal variable (non-secret for testing permissions, not encryption)
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/alice/my_variable', 'alice_value', false,
|
||||
'Alice variable', '{}');
|
||||
|
||||
-- Bob's personal variable (non-secret for testing permissions, not encryption)
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/bob/my_variable', 'bob_value', false,
|
||||
'Bob variable', '{}');
|
||||
|
||||
-- ============================================
|
||||
-- SCHEDULES
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal schedule
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms)
|
||||
VALUES ('test-workspace', 'u/alice/my_schedule', 'alice', NOW(), '0 * * * *', false,
|
||||
'u/alice/my_script', '{}', false, 'alice@windmill.dev', 'UTC', '{}');
|
||||
|
||||
-- Bob's personal schedule
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms)
|
||||
VALUES ('test-workspace', 'u/bob/my_schedule', 'bob', NOW(), '0 * * * *', false,
|
||||
'u/bob/my_script', '{}', false, 'bob@windmill.dev', 'UTC', '{}');
|
||||
|
||||
-- ============================================
|
||||
-- APPS (with app_versions)
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal app
|
||||
INSERT INTO app (id, workspace_id, path, summary, versions, policy, extra_perms)
|
||||
VALUES (2001, 'test-workspace', 'u/alice/my_app', 'Alice app', '{}',
|
||||
'{"on_behalf_of": "u/alice", "on_behalf_of_email": "alice@windmill.dev", "execution_mode": "viewer"}', '{}');
|
||||
|
||||
-- Shared folder app
|
||||
INSERT INTO app (id, workspace_id, path, summary, versions, policy, extra_perms)
|
||||
VALUES (2002, 'test-workspace', 'f/shared/shared_app', 'Shared app', '{}',
|
||||
'{"on_behalf_of": "u/admin", "on_behalf_of_email": "admin@windmill.dev", "execution_mode": "viewer"}', '{}');
|
||||
|
||||
-- Create app versions
|
||||
INSERT INTO app_version (id, app_id, value, created_by, created_at)
|
||||
VALUES
|
||||
(2001, 2001, '{"grid": []}', 'alice', NOW()),
|
||||
(2002, 2002, '{"grid": []}', 'admin', NOW());
|
||||
|
||||
-- Update apps with version references
|
||||
UPDATE app SET versions = ARRAY[2001::bigint] WHERE id = 2001;
|
||||
UPDATE app SET versions = ARRAY[2002::bigint] WHERE id = 2002;
|
||||
69
backend/windmill-api-integration-tests/tests/fixtures/resources_test.sql
vendored
Normal file
69
backend/windmill-api-integration-tests/tests/fixtures/resources_test.sql
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
-- Combined fixture for resource endpoint tests
|
||||
|
||||
-- === resource type test data ===
|
||||
|
||||
INSERT INTO resource_type (workspace_id, name, schema, description, created_by)
|
||||
VALUES ('test-workspace', 'test_db', '{"type": "object", "properties": {"host": {"type": "string"}}}',
|
||||
'Test DB type', 'test-user');
|
||||
|
||||
-- === get_value_interpolated test data ===
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/simple_resource', '{"host": "localhost", "port": 5432}',
|
||||
'Simple resource', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/test-user/db_password', 'hunter2', false, 'DB password', '{}');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/resource_with_var', '{"host": "localhost", "password": "$var:u/test-user/db_password"}',
|
||||
'Resource with var ref', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/db_credentials', '{"user": "admin", "password": "secret123"}',
|
||||
'DB credentials', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/resource_with_res', '{"host": "localhost", "credentials": "$res:u/test-user/db_credentials"}',
|
||||
'Resource with res ref', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/resource_mixed', '{"host": "localhost", "password": "$var:u/test-user/db_password", "credentials": "$res:u/test-user/db_credentials"}',
|
||||
'Resource with mixed refs', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/null_resource', null,
|
||||
'Null resource', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/test-user/api_key', 'sk-abc123', false, 'API key', '{}');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/inner_resource', '{"key": "$var:u/test-user/api_key"}',
|
||||
'Inner resource', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/chained_resource', '{"service": "myapi", "auth": "$res:u/test-user/inner_resource"}',
|
||||
'Chained resource', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/resource_with_array', '{"hosts": ["host1", "host2"], "port": 5432}',
|
||||
'Resource with array', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/scalar_var_resource', '"$var:u/test-user/db_password"',
|
||||
'Scalar var ref', 'string', '{}', 'test-user');
|
||||
|
||||
-- === mcp_tools test data ===
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/mcp_valid', '{"name": "test-mcp", "url": "http://127.0.0.1:19999/mcp"}',
|
||||
'Valid MCP resource (unreachable)', 'mcp_server', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/mcp_invalid_format', '{"host": "localhost", "port": 5432}',
|
||||
'Not an MCP resource', 'object', '{}', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/mcp_null', null,
|
||||
'Null MCP resource', 'mcp_server', '{}', 'test-user');
|
||||
10
backend/windmill-api-integration-tests/tests/fixtures/variables_test.sql
vendored
Normal file
10
backend/windmill-api-integration-tests/tests/fixtures/variables_test.sql
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
-- Fixture for variable endpoint tests
|
||||
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/test-user/plain_var', 'hello world', false, 'A plain variable', '{}');
|
||||
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/test-user/secret_var', 'supersecret', true, 'A secret variable', '{}');
|
||||
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/test-user/another_var', 'foobar', false, 'Another variable', '{}');
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn flow_url(port: u16, endpoint: &str, path: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/flows/{endpoint}/{path}")
|
||||
@@ -40,7 +39,7 @@ fn new_flow(path: &str, summary: &str) -> serde_json::Value {
|
||||
})
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_flow_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn folder_url(port: u16, endpoint: &str, name: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/folders/{endpoint}/{name}")
|
||||
@@ -16,7 +15,7 @@ fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
builder.header("Authorization", "Bearer SECRET_TOKEN")
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_folder_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn group_url(port: u16, endpoint: &str, name: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/groups/{endpoint}/{name}")
|
||||
@@ -16,7 +15,7 @@ fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
builder.header("Authorization", "Bearer SECRET_TOKEN")
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_group_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,10 +1,13 @@
|
||||
#[cfg(feature = "deno_core")]
|
||||
use serde_json::json;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use windmill_test_utils::*;
|
||||
|
||||
/// Helper to create a client authenticated as a specific user
|
||||
#[cfg(feature = "deno_core")]
|
||||
async fn create_client_for_user(_port: u16, token: &str) -> reqwest::Client {
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
@@ -18,12 +21,14 @@ async fn create_client_for_user(_port: u16, token: &str) -> reqwest::Client {
|
||||
}
|
||||
|
||||
/// Test helper to check if a GET request succeeds
|
||||
#[cfg(feature = "deno_core")]
|
||||
async fn can_read(client: &reqwest::Client, url: &str) -> bool {
|
||||
let resp = client.get(url).send().await.unwrap();
|
||||
resp.status().is_success()
|
||||
}
|
||||
|
||||
/// Test helper to check if a POST request succeeds (for write operations)
|
||||
#[cfg(feature = "deno_core")]
|
||||
async fn can_write(client: &reqwest::Client, url: &str, body: serde_json::Value) -> bool {
|
||||
let resp = client.post(url).json(&body).send().await.unwrap();
|
||||
let status = resp.status();
|
||||
@@ -44,7 +49,7 @@ async fn can_write(client: &reqwest::Client, url: &str, body: serde_json::Value)
|
||||
/// `cargo test --features deno_core permissions -- --ignored`
|
||||
#[ignore]
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "permissions_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "permissions_test"))]
|
||||
async fn test_permissions_exhaustive(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -477,7 +482,7 @@ async fn test_permissions_exhaustive(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// Additional test for verifying group permission inheritance
|
||||
#[ignore]
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "permissions_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "permissions_test"))]
|
||||
async fn test_group_permission_inheritance(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -547,7 +552,7 @@ async fn test_group_permission_inheritance(db: Pool<Postgres>) -> anyhow::Result
|
||||
/// Test that permissions work correctly for all item types
|
||||
#[ignore]
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "permissions_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "permissions_test"))]
|
||||
async fn test_all_item_types_permissions(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -588,7 +593,7 @@ async fn test_all_item_types_permissions(db: Pool<Postgres>) -> anyhow::Result<(
|
||||
/// Operators have limited permissions - they can execute but cannot manage resources
|
||||
#[ignore]
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "permissions_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "permissions_test"))]
|
||||
async fn test_operator_cannot_create_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -7,8 +7,7 @@ use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::workspaces::invalidate_protection_rules_cache;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn client() -> reqwest::Client {
|
||||
reqwest::Client::new()
|
||||
@@ -51,7 +50,7 @@ fn new_flow(path: &str, summary: &str) -> serde_json::Value {
|
||||
|
||||
/// Comprehensive test for protection rules functionality.
|
||||
/// Tests all essential cases in a single test to avoid cache interference.
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_protection_rules(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
invalidate_protection_rules_cache("test-workspace");
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn resource_url(port: u16, endpoint: &str, path: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/resources/{endpoint}/{path}")
|
||||
@@ -23,7 +22,7 @@ async fn authed_get(port: u16, endpoint: &str, path: &str) -> reqwest::Response
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "resources_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "resources_test"))]
|
||||
async fn test_resource_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -400,7 +399,7 @@ async fn test_resource_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "mcp")]
|
||||
#[sqlx::test(fixtures("base", "resources_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "resources_test"))]
|
||||
async fn test_mcp_tools(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn schedule_url(port: u16, endpoint: &str, path: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/schedules/{endpoint}/{path}")
|
||||
@@ -23,7 +22,7 @@ async fn authed_get(port: u16, endpoint: &str, path: &str) -> reqwest::Response
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_schedule_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn script_url(port: u16, endpoint: &str, path: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/scripts/{endpoint}/{path}")
|
||||
@@ -39,7 +38,7 @@ fn new_script(path: &str, summary: &str, content: &str) -> serde_json::Value {
|
||||
})
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_script_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -24,8 +24,7 @@ use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use std::time::Duration;
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
// ============================================================================
|
||||
// Helpers
|
||||
@@ -137,7 +136,7 @@ async fn insert_resource(
|
||||
/// -- --ignored --nocapture
|
||||
/// ```
|
||||
#[ignore = "requires running MQTT broker on localhost:1883"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_mqtt_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -230,7 +229,7 @@ async fn test_mqtt_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// -- --ignored --nocapture
|
||||
/// ```
|
||||
#[ignore = "requires running WebSocket echo server on localhost:8765"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_websocket_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -286,7 +285,7 @@ async fn test_websocket_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// -- --ignored --nocapture
|
||||
/// ```
|
||||
#[ignore = "requires PostgreSQL with wal_level=logical"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_postgres_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -387,7 +386,7 @@ async fn test_postgres_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// ```
|
||||
#[cfg(all(feature = "enterprise", feature = "private"))]
|
||||
#[ignore = "requires running Kafka broker on localhost:9092"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_kafka_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -478,7 +477,7 @@ async fn test_kafka_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// ```
|
||||
#[cfg(all(feature = "enterprise", feature = "private"))]
|
||||
#[ignore = "requires running NATS server on localhost:4222"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_nats_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -553,7 +552,7 @@ async fn test_nats_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// ```
|
||||
#[cfg(all(feature = "enterprise", feature = "private"))]
|
||||
#[ignore = "requires LocalStack SQS on localhost:4566"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_sqs_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -641,7 +640,7 @@ async fn test_sqs_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
/// ```
|
||||
#[cfg(all(feature = "enterprise", feature = "private"))]
|
||||
#[ignore = "requires GCP Pub/Sub emulator on localhost:8085"]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_gcp_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -12,14 +12,13 @@ use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
// ============================================================================
|
||||
// Capture Config Tests (direct DB)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_config_insert_and_query(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -56,7 +55,7 @@ async fn test_capture_config_insert_and_query(db: Pool<Postgres>) -> anyhow::Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_config_upsert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -104,7 +103,7 @@ async fn test_capture_config_upsert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_config_ping_updates_timestamp(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -159,7 +158,7 @@ async fn test_capture_config_ping_updates_timestamp(db: Pool<Postgres>) -> anyho
|
||||
// Capture Payload Tests (direct DB)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_insert_and_list(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
for i in 0..2 {
|
||||
sqlx::query!(
|
||||
@@ -192,7 +191,7 @@ async fn test_capture_insert_and_list(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let id = sqlx::query_scalar!(
|
||||
r#"
|
||||
@@ -227,7 +226,7 @@ async fn test_capture_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_filter_by_trigger_kind(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -304,7 +303,7 @@ struct CaptureResponse {
|
||||
main_args: serde_json::Value,
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_api_set_config_and_list(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -348,7 +347,7 @@ async fn test_capture_api_set_config_and_list(db: Pool<Postgres>) -> anyhow::Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_api_list_captures(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -394,7 +393,7 @@ async fn test_capture_api_list_captures(db: Pool<Postgres>) -> anyhow::Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_api_get_single(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -441,7 +440,7 @@ async fn test_capture_api_get_single(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_api_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -493,7 +492,7 @@ async fn test_capture_api_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_capture_api_pagination(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -555,7 +554,7 @@ async fn test_capture_api_pagination(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// HTTP Trigger Tests (direct DB)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_http_trigger_insert_and_query(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -610,7 +609,7 @@ async fn test_http_trigger_insert_and_query(db: Pool<Postgres>) -> anyhow::Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_http_trigger_multiple_methods(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let methods = ["get", "post", "put", "delete", "patch"];
|
||||
|
||||
@@ -657,7 +656,7 @@ async fn test_http_trigger_multiple_methods(db: Pool<Postgres>) -> anyhow::Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_http_trigger_authentication_methods(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let auth_methods = ["none", "windmill", "api_key", "basic_http", "signature"];
|
||||
|
||||
@@ -708,7 +707,7 @@ async fn test_http_trigger_authentication_methods(db: Pool<Postgres>) -> anyhow:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_http_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -761,7 +760,7 @@ async fn test_http_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_http_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -813,7 +812,7 @@ async fn test_http_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_http_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let modes = ["enabled", "disabled", "suspended"];
|
||||
|
||||
@@ -870,7 +869,7 @@ async fn test_http_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<
|
||||
// Other Trigger Types Tests (DB schema validation)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_websocket_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -909,7 +908,7 @@ async fn test_websocket_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_kafka_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -952,7 +951,7 @@ async fn test_kafka_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_postgres_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -995,7 +994,7 @@ async fn test_postgres_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_nats_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1038,7 +1037,7 @@ async fn test_nats_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_sqs_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1089,7 +1088,7 @@ async fn test_sqs_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// Cross-trigger tests
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_server_state_tracking(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1144,7 +1143,7 @@ async fn test_trigger_server_state_tracking(db: Pool<Postgres>) -> anyhow::Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let modes = ["enabled", "disabled", "enabled"];
|
||||
|
||||
@@ -1191,7 +1190,7 @@ async fn test_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_multiple_capture_configs_per_path(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let trigger_kinds = ["webhook", "email", "kafka"];
|
||||
|
||||
@@ -1229,7 +1228,7 @@ async fn test_multiple_capture_configs_per_path(db: Pool<Postgres>) -> anyhow::R
|
||||
// Schedule Tests (DB-level)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_schedule_insert_and_query(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1277,7 +1276,7 @@ async fn test_schedule_insert_and_query(db: Pool<Postgres>) -> anyhow::Result<()
|
||||
// MQTT Trigger Tests (DB-level)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_mqtt_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -1321,7 +1320,7 @@ async fn test_mqtt_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_mqtt_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -1366,7 +1365,7 @@ async fn test_mqtt_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_mqtt_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -1414,7 +1413,7 @@ async fn test_mqtt_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// GCP Trigger Tests (DB-level)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_gcp_trigger_insert_pull(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -1465,7 +1464,7 @@ async fn test_gcp_trigger_insert_pull(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_gcp_trigger_insert_push(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -1515,7 +1514,7 @@ async fn test_gcp_trigger_insert_push(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_gcp_trigger_unique_constraint(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let insert_query = r#"
|
||||
INSERT INTO gcp_trigger (
|
||||
@@ -1569,7 +1568,7 @@ async fn test_gcp_trigger_unique_constraint(db: Pool<Postgres>) -> anyhow::Resul
|
||||
// Email Trigger Tests (DB-level)
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_email_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1612,7 +1611,7 @@ async fn test_email_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_email_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1658,7 +1657,7 @@ async fn test_email_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_email_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn user_url(port: u16, endpoint: &str, name: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/users/{endpoint}/{name}")
|
||||
@@ -16,7 +15,7 @@ fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
builder.header("Authorization", "Bearer SECRET_TOKEN")
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_user_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn variable_url(port: u16, endpoint: &str, path: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/variables/{endpoint}/{path}")
|
||||
@@ -23,7 +22,7 @@ async fn authed_get(port: u16, endpoint: &str, path: &str) -> reqwest::Response
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "variables_test"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "variables_test"))]
|
||||
async fn test_variable_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
/// Comprehensive integration test for the compare_workspaces endpoint.
|
||||
///
|
||||
@@ -12,7 +11,7 @@ use common::*;
|
||||
/// 3. Making various changes in both workspaces (new items, modifications, conflicts, deletions, renames)
|
||||
/// 4. Populating the workspace_diff table to simulate Git sync tracking
|
||||
/// 5. Calling compare_workspaces and verifying all aspects of the comparison
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_compare_workspaces_comprehensive(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
fn client() -> reqwest::Client {
|
||||
reqwest::Client::new()
|
||||
@@ -12,7 +11,7 @@ fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
builder.header("Authorization", "Bearer SECRET_TOKEN")
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_workspace_endpoints(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
@@ -6,5 +6,4 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
pub mod flows;
|
||||
pub mod scripts;
|
||||
|
||||
@@ -11,7 +11,7 @@ path = "src/lib.rs"
|
||||
[features]
|
||||
default = []
|
||||
private = ["windmill-audit/private", "windmill-common/private", "windmill-api-auth/private", "windmill-store/private", "windmill-api-users/private", "windmill-api-workspaces/private", "windmill-api-groups/private", "windmill-api-configs/private", "windmill-api-settings/private", "windmill-api-agent-workers?/private", "windmill-trigger-kafka?/private", "windmill-trigger-postgres?/private", "windmill-trigger-mqtt?/private", "windmill-trigger-websocket?/private", "windmill-trigger-nats?/private", "windmill-trigger-sqs?/private", "windmill-trigger-gcp?/private", "windmill-trigger-email?/private"]
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-api-configs/enterprise", "windmill-api-settings/enterprise", "windmill-api-agent-workers?/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise"]
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-flows/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-api-configs/enterprise", "windmill-api-settings/enterprise", "windmill-api-agent-workers?/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise"]
|
||||
stripe = []
|
||||
inline_preview = ["dep:windmill-worker", "windmill-api-configs/inline_preview"]
|
||||
agent_worker_server = ["dep:windmill-worker", "dep:windmill-api-agent-workers"]
|
||||
@@ -48,6 +48,7 @@ quickjs = ["windmill-jseval/quickjs"]
|
||||
windmill-mcp = { workspace = true, optional = true }
|
||||
windmill-api-auth.workspace = true
|
||||
windmill-api-scripts.workspace = true
|
||||
windmill-api-flows.workspace = true
|
||||
windmill-api-users.workspace = true
|
||||
windmill-api-workspaces.workspace = true
|
||||
windmill-api-groups.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.631.0
|
||||
version: 1.631.2
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
// Re-export everything from windmill-api-scripts
|
||||
pub use windmill_api_scripts::flows::*;
|
||||
// Re-export everything from windmill-api-flows
|
||||
pub use windmill_api_flows::flows::*;
|
||||
|
||||
use crate::triggers::{get_triggers_count_internal, TriggersCount};
|
||||
use axum::{
|
||||
@@ -20,7 +20,7 @@ use windmill_common::{error::JsonResult, utils::StripPath, DB};
|
||||
/// Wraps the subcrate's workspaced_service with the trigger count route
|
||||
/// that depends on windmill-api internals.
|
||||
pub fn workspaced_service() -> Router {
|
||||
windmill_api_scripts::flows::workspaced_service()
|
||||
windmill_api_flows::flows::workspaced_service()
|
||||
.route("/get_triggers_count/*path", get(get_triggers_count))
|
||||
}
|
||||
|
||||
|
||||
146
backend/windmill-common/tests/fixtures/base.sql
vendored
Normal file
146
backend/windmill-common/tests/fixtures/base.sql
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
-- used for backend automated testing
|
||||
-- https://docs.rs/sqlx/latest/sqlx/attr.test.html
|
||||
|
||||
INSERT INTO workspace
|
||||
(id, name, owner)
|
||||
VALUES ('test-workspace', 'test-workspace', 'test-user');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test@windmill.dev', 'test-user', true, 'Admin');
|
||||
|
||||
INSERT INTO workspace_key(workspace_id, kind, key) VALUES
|
||||
('test-workspace', 'cloud', 'test-key');
|
||||
|
||||
|
||||
INSERT INTO workspace_settings (workspace_id) VALUES
|
||||
('test-workspace');
|
||||
|
||||
INSERT INTO group_ (workspace_id, name, summary, extra_perms) VALUES
|
||||
('test-workspace', 'all', 'All users', '{}');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name, username)
|
||||
VALUES ('test@windmill.dev', 'not-a-real-hash', 'password', true, true, 'Test User', 'test-user');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES ('test2@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 2');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES ('test3@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 3');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test2@windmill.dev', 'test-user-2', false, 'User');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test3@windmill.dev', 'test-user-3', false, 'User');
|
||||
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN', 'test@windmill.dev', 'test token', true);
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN_2', 'test2@windmill.dev', 'test token 2', false);
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN_3', 'test3@windmill.dev', 'test token 3', false);
|
||||
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_admin;
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_user;
|
||||
|
||||
CREATE FUNCTION "notify_insert_on_completed_job" ()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('completed', NEW.id::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER "notify_insert_on_completed_job"
|
||||
AFTER INSERT ON "v2_job_completed"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION "notify_insert_on_completed_job" ();
|
||||
|
||||
|
||||
CREATE FUNCTION "notify_queue" ()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('queued', NEW.id::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER "notify_queue_after_insert"
|
||||
AFTER INSERT ON "v2_job_queue"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION "notify_queue" ();
|
||||
|
||||
CREATE TRIGGER "notify_queue_after_flow_status_update"
|
||||
AFTER UPDATE ON "v2_job_status"
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.flow_status IS DISTINCT FROM OLD.flow_status)
|
||||
EXECUTE FUNCTION "notify_queue" ();
|
||||
|
||||
-- Apply phase 4:
|
||||
DROP FUNCTION IF EXISTS v2_job_after_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_completed_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_completed_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_after_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_runtime_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_runtime_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_status_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_status_before_update CASCADE;
|
||||
|
||||
DROP VIEW IF EXISTS completed_job, completed_job_view, job, queue, queue_view CASCADE;
|
||||
|
||||
ALTER TABLE v2_job_queue
|
||||
DROP COLUMN IF EXISTS __parent_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_by CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_hash CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __args CASCADE,
|
||||
DROP COLUMN IF EXISTS __logs CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_code CASCADE,
|
||||
DROP COLUMN IF EXISTS __canceled CASCADE,
|
||||
DROP COLUMN IF EXISTS __last_ping CASCADE,
|
||||
DROP COLUMN IF EXISTS __job_kind CASCADE,
|
||||
DROP COLUMN IF EXISTS __env_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __schedule_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __permissioned_as CASCADE,
|
||||
DROP COLUMN IF EXISTS __flow_status CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_flow CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_flow_step CASCADE,
|
||||
DROP COLUMN IF EXISTS __language CASCADE,
|
||||
DROP COLUMN IF EXISTS __same_worker CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_lock CASCADE,
|
||||
DROP COLUMN IF EXISTS __pre_run_error CASCADE,
|
||||
DROP COLUMN IF EXISTS __email CASCADE,
|
||||
DROP COLUMN IF EXISTS __visible_to_owner CASCADE,
|
||||
DROP COLUMN IF EXISTS __mem_peak CASCADE,
|
||||
DROP COLUMN IF EXISTS __root_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __leaf_jobs CASCADE,
|
||||
DROP COLUMN IF EXISTS __concurrent_limit CASCADE,
|
||||
DROP COLUMN IF EXISTS __concurrency_time_window_s CASCADE,
|
||||
DROP COLUMN IF EXISTS __timeout CASCADE,
|
||||
DROP COLUMN IF EXISTS __flow_step_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __cache_ttl CASCADE;
|
||||
|
||||
LOCK TABLE v2_job_queue IN ACCESS EXCLUSIVE MODE;
|
||||
ALTER TABLE v2_job_completed
|
||||
DROP COLUMN IF EXISTS __parent_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_by CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_at CASCADE,
|
||||
DROP COLUMN IF EXISTS __success CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_hash CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __args CASCADE,
|
||||
DROP COLUMN IF EXISTS __logs CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_code CASCADE,
|
||||
DROP COLUMN IF EXISTS __canceled CASCADE,
|
||||
DROP COLUMN IF EXISTS __job_kind CASCADE,
|
||||
DROP COLUMN IF EXISTS __env_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __schedule_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __permissioned_as CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_flow CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_flow_step CASCADE,
|
||||
DROP COLUMN IF EXISTS __language CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_skipped CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_lock CASCADE,
|
||||
DROP COLUMN IF EXISTS __email CASCADE,
|
||||
DROP COLUMN IF EXISTS __visible_to_owner CASCADE,
|
||||
DROP COLUMN IF EXISTS __tag CASCADE,
|
||||
DROP COLUMN IF EXISTS __priority CASCADE;
|
||||
76
backend/windmill-common/tests/fixtures/instance_group_auto_add.sql
vendored
Normal file
76
backend/windmill-common/tests/fixtures/instance_group_auto_add.sql
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
-- Fixture for instance group auto-add tests
|
||||
-- Sets up test data for testing the interaction between instance groups and workspace auto-add
|
||||
|
||||
-- Create additional workspaces for testing
|
||||
INSERT INTO workspace (id, name, owner)
|
||||
VALUES
|
||||
('ws-with-auto-add', 'Workspace with Auto Add', 'admin'),
|
||||
('ws-no-auto-add', 'Workspace without Auto Add', 'admin'),
|
||||
('ws-multi-group', 'Workspace with Multiple Groups', 'admin')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO workspace_settings (workspace_id)
|
||||
VALUES
|
||||
('ws-with-auto-add'),
|
||||
('ws-no-auto-add'),
|
||||
('ws-multi-group')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO workspace_key(workspace_id, kind, key)
|
||||
VALUES
|
||||
('ws-with-auto-add', 'cloud', 'key-auto-add'),
|
||||
('ws-no-auto-add', 'cloud', 'key-no-auto'),
|
||||
('ws-multi-group', 'cloud', 'key-multi')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Create instance groups
|
||||
INSERT INTO instance_group (name, summary, id)
|
||||
VALUES
|
||||
('engineering', 'Engineering team', 'eng-uuid-001'),
|
||||
('sales', 'Sales team', 'sales-uuid-002'),
|
||||
('admins', 'Admin group', 'admin-uuid-003')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Create users in the password table (required for auto-add eligibility)
|
||||
INSERT INTO password (email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES
|
||||
('alice@example.com', 'hash1', 'password', false, true, 'Alice'),
|
||||
('bob@example.com', 'hash2', 'password', false, true, 'Bob'),
|
||||
('charlie@example.com', 'hash3', 'password', false, true, 'Charlie'),
|
||||
('dave@example.com', 'hash4', 'password', false, true, 'Dave'),
|
||||
('admin@windmill.dev', 'hash5', 'password', true, true, 'Admin')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Add users to instance groups
|
||||
INSERT INTO email_to_igroup (email, igroup)
|
||||
VALUES
|
||||
('alice@example.com', 'engineering'),
|
||||
('bob@example.com', 'engineering'),
|
||||
('bob@example.com', 'sales'),
|
||||
('charlie@example.com', 'sales'),
|
||||
('dave@example.com', 'admins')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Create admin user in workspaces for running tests
|
||||
INSERT INTO usr (workspace_id, username, email, is_admin, operator)
|
||||
VALUES
|
||||
('ws-with-auto-add', 'admin', 'admin@windmill.dev', true, false),
|
||||
('ws-no-auto-add', 'admin', 'admin@windmill.dev', true, false),
|
||||
('ws-multi-group', 'admin', 'admin@windmill.dev', true, false)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Create the 'all' group for each workspace (required for user auto-add)
|
||||
INSERT INTO group_ (workspace_id, name, summary, extra_perms)
|
||||
VALUES
|
||||
('ws-with-auto-add', 'all', 'All users', '{}'),
|
||||
('ws-no-auto-add', 'all', 'All users', '{}'),
|
||||
('ws-multi-group', 'all', 'All users', '{}')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Add admin to the 'all' group
|
||||
INSERT INTO usr_to_group (workspace_id, usr, group_)
|
||||
VALUES
|
||||
('ws-with-auto-add', 'admin', 'all'),
|
||||
('ws-no-auto-add', 'admin', 'all'),
|
||||
('ws-multi-group', 'admin', 'all')
|
||||
ON CONFLICT DO NOTHING;
|
||||
31
backend/windmill-common/tests/fixtures/secret_backend.sql
vendored
Normal file
31
backend/windmill-common/tests/fixtures/secret_backend.sql
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
-- Fixture for secret backend migration tests
|
||||
-- Sets up test secrets in the variable table
|
||||
|
||||
-- Create a second workspace for testing workspace isolation
|
||||
INSERT INTO workspace (id, name, owner)
|
||||
VALUES ('test-workspace-2', 'test-workspace-2', 'test-user')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO workspace_settings (workspace_id)
|
||||
VALUES ('test-workspace-2')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO workspace_key(workspace_id, kind, key)
|
||||
VALUES ('test-workspace-2', 'cloud', 'test-key-2')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Insert test variables with placeholder values.
|
||||
-- Secret values are encrypted by the test setup using build_crypt + encrypt
|
||||
-- with the workspace key, matching production behavior.
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES
|
||||
('test-workspace', 'u/test-user/db_password', 'PLACEHOLDER', true, 'Database password', '{}'),
|
||||
('test-workspace', 'u/test-user/api_key', 'PLACEHOLDER', true, 'API key for external service', '{}'),
|
||||
('test-workspace', 'u/test-user/public_var', 'not-a-secret', false, 'A non-secret variable', '{}')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Insert test secrets for workspace 2 (to test isolation)
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES
|
||||
('test-workspace-2', 'u/test-user/other_secret', 'PLACEHOLDER', true, 'Secret in workspace 2', '{}')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -31,7 +31,7 @@ mod tests {
|
||||
|
||||
/// Test that configuring instance groups for a workspace auto-adds existing group members
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_configure_instance_groups_adds_existing_members(db: Pool<Postgres>) {
|
||||
// Configure workspace to auto-add users from 'engineering' group with 'developer' role
|
||||
let groups = vec!["engineering".to_string()];
|
||||
@@ -158,7 +158,7 @@ mod tests {
|
||||
|
||||
/// Test role assignment based on instance group configuration
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_role_assignment_admin(db: Pool<Postgres>) {
|
||||
// Configure workspace with admins group having admin role
|
||||
let groups = vec!["admins".to_string()];
|
||||
@@ -209,7 +209,7 @@ mod tests {
|
||||
|
||||
/// Test role assignment for operator
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_role_assignment_operator(db: Pool<Postgres>) {
|
||||
// Configure workspace with sales group having operator role
|
||||
let groups = vec!["sales".to_string()];
|
||||
@@ -260,7 +260,7 @@ mod tests {
|
||||
|
||||
/// Test role precedence when user is in multiple instance groups
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_role_precedence_multiple_groups(db: Pool<Postgres>) {
|
||||
// Configure workspace with multiple groups: engineering (admin), sales (operator)
|
||||
// Bob is in both groups, should get admin role (highest precedence)
|
||||
@@ -322,7 +322,7 @@ mod tests {
|
||||
|
||||
/// Test removing user from instance group removes them from workspace
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_remove_user_from_instance_group(db: Pool<Postgres>) {
|
||||
// First, add alice to the workspace via engineering group
|
||||
let added_via = json!({"source": "instance_group", "group": "engineering"});
|
||||
@@ -416,7 +416,7 @@ mod tests {
|
||||
|
||||
/// Test that users added via domain are not affected by instance group removal
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_domain_added_users_not_affected_by_group_removal(db: Pool<Postgres>) {
|
||||
// Add alice via domain (not instance group)
|
||||
let added_via = json!({"source": "domain", "domain": "example.com"});
|
||||
@@ -469,7 +469,7 @@ mod tests {
|
||||
|
||||
/// Test cleanup when instance group is removed from workspace configuration
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_cleanup_removed_instance_groups(db: Pool<Postgres>) {
|
||||
// First, add users via engineering group
|
||||
for (username, email) in &[("alice", "alice@example.com"), ("bob", "bob@example.com")] {
|
||||
@@ -588,7 +588,7 @@ mod tests {
|
||||
|
||||
/// Test that users are not duplicated if already in workspace
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_no_duplicate_users(db: Pool<Postgres>) {
|
||||
// Add alice to workspace first (without instance group tracking)
|
||||
sqlx::query!(
|
||||
@@ -636,7 +636,7 @@ mod tests {
|
||||
|
||||
/// Test workspace without auto-add configured is not affected
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_workspace_without_auto_add_not_affected(db: Pool<Postgres>) {
|
||||
// ws-no-auto-add has no instance_groups configured
|
||||
|
||||
@@ -672,7 +672,7 @@ mod tests {
|
||||
|
||||
/// Test querying workspaces configured with a specific instance group
|
||||
#[ignore = "requires database setup - run with --ignored flag"]
|
||||
#[sqlx::test(fixtures("base", "instance_group_auto_add"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "instance_group_auto_add"))]
|
||||
async fn test_query_workspaces_with_instance_group(db: Pool<Postgres>) {
|
||||
// Configure ws-with-auto-add to use engineering group
|
||||
let groups = vec!["engineering".to_string()];
|
||||
@@ -11,8 +11,6 @@
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::notify_events::{cleanup_old_events, get_latest_event_id, poll_notify_events};
|
||||
|
||||
mod common;
|
||||
|
||||
/// Helper to insert a test event directly
|
||||
async fn insert_test_event(db: &Pool<Postgres>, channel: &str, payload: &str) -> i64 {
|
||||
sqlx::query_scalar::<_, i64>(
|
||||
@@ -39,7 +37,7 @@ async fn count_events_for_channel(db: &Pool<Postgres>, channel: &str) -> i64 {
|
||||
// Basic Functionality Tests
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_get_latest_event_id_returns_valid_id(db: Pool<Postgres>) {
|
||||
// Get current latest id
|
||||
let latest_id = get_latest_event_id(&db).await.expect("Should get latest event id");
|
||||
@@ -51,7 +49,7 @@ async fn test_get_latest_event_id_returns_valid_id(db: Pool<Postgres>) {
|
||||
assert!(new_latest_id >= new_id, "Latest id should be >= new event id");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_get_latest_event_id_with_events(db: Pool<Postgres>) {
|
||||
let _id1 = insert_test_event(&db, "test_channel_1", "payload1").await;
|
||||
let _id2 = insert_test_event(&db, "test_channel_2", "payload2").await;
|
||||
@@ -61,7 +59,7 @@ async fn test_get_latest_event_id_with_events(db: Pool<Postgres>) {
|
||||
assert!(latest_id >= id3, "Latest id should be >= last inserted id");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_poll_notify_events_no_new_events(db: Pool<Postgres>) {
|
||||
// Get latest id first
|
||||
let latest_id = get_latest_event_id(&db).await.unwrap();
|
||||
@@ -71,7 +69,7 @@ async fn test_poll_notify_events_no_new_events(db: Pool<Postgres>) {
|
||||
assert!(events.is_empty(), "Should return empty vec when polling from latest id");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_poll_notify_events_returns_new_events(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -92,7 +90,7 @@ async fn test_poll_notify_events_returns_new_events(db: Pool<Postgres>) {
|
||||
assert!(our_events[0].id < our_events[1].id, "Events should be ordered by id ascending");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_poll_notify_events_respects_last_event_id(db: Pool<Postgres>) {
|
||||
let id1 = insert_test_event(&db, "test_respect_id", "payload1").await;
|
||||
let _id2 = insert_test_event(&db, "test_respect_id", "payload2").await;
|
||||
@@ -109,7 +107,7 @@ async fn test_poll_notify_events_respects_last_event_id(db: Pool<Postgres>) {
|
||||
assert!(our_events.iter().all(|e| e.id > id1), "All events should have id > id1");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_cleanup_old_events(db: Pool<Postgres>) {
|
||||
// Use unique channel names to avoid interference from other tests
|
||||
let old_channel = format!("test_cleanup_old_{}", uuid::Uuid::new_v4());
|
||||
@@ -156,7 +154,7 @@ async fn test_cleanup_old_events(db: Pool<Postgres>) {
|
||||
// Database Trigger Tests - Verify triggers insert events correctly
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_config_change(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -178,7 +176,7 @@ async fn test_trigger_notify_config_change(db: Pool<Postgres>) {
|
||||
assert!(!config_events.is_empty(), "Should have notify_config_change event");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_global_setting_change_insert(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -201,7 +199,7 @@ async fn test_trigger_notify_global_setting_change_insert(db: Pool<Postgres>) {
|
||||
assert!(!setting_events.is_empty(), "Should have notify_global_setting_change event on insert");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_global_setting_change_update(db: Pool<Postgres>) {
|
||||
// Use a unique setting name for testing
|
||||
let setting_name = format!("test_setting_update_{}", uuid::Uuid::new_v4());
|
||||
@@ -231,7 +229,7 @@ async fn test_trigger_notify_global_setting_change_update(db: Pool<Postgres>) {
|
||||
assert!(!setting_events.is_empty(), "Should have notify_global_setting_change event on update");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_global_setting_change_delete(db: Pool<Postgres>) {
|
||||
// Use a unique setting name for testing
|
||||
let setting_name = format!("test_setting_delete_{}", uuid::Uuid::new_v4());
|
||||
@@ -261,7 +259,7 @@ async fn test_trigger_notify_global_setting_change_delete(db: Pool<Postgres>) {
|
||||
assert!(!setting_events.is_empty(), "Should have notify_global_setting_change event on delete");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_workspace_envs_change(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -283,7 +281,7 @@ async fn test_trigger_notify_workspace_envs_change(db: Pool<Postgres>) {
|
||||
assert!(!env_events.is_empty(), "Should have notify_workspace_envs_change event");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_workspace_key_change(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -305,7 +303,7 @@ async fn test_trigger_notify_workspace_key_change(db: Pool<Postgres>) {
|
||||
assert!(!key_events.is_empty(), "Should have notify_workspace_key_change event");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_token_invalidation(db: Pool<Postgres>) {
|
||||
// First insert a session token
|
||||
let token = format!("test_token_{}", uuid::Uuid::new_v4());
|
||||
@@ -336,7 +334,7 @@ async fn test_trigger_notify_token_invalidation(db: Pool<Postgres>) {
|
||||
assert!(!token_events.is_empty(), "Should have notify_token_invalidation event");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_webhook_change(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -355,7 +353,7 @@ async fn test_trigger_notify_webhook_change(db: Pool<Postgres>) {
|
||||
assert!(!webhook_events.is_empty(), "Should have notify_webhook_change event");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_workspace_premium_change(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -378,7 +376,7 @@ async fn test_trigger_notify_workspace_premium_change(db: Pool<Postgres>) {
|
||||
// HTTP Trigger Tests
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_http_trigger_change(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -408,7 +406,7 @@ async fn test_trigger_notify_http_trigger_change(db: Pool<Postgres>) {
|
||||
// Script/Flow Version Change Tests
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_runnable_version_change_script(db: Pool<Postgres>) {
|
||||
// First create a script without lock
|
||||
let script_path = format!("f/test/script_{}", uuid::Uuid::new_v4());
|
||||
@@ -449,7 +447,7 @@ async fn test_trigger_notify_runnable_version_change_script(db: Pool<Postgres>)
|
||||
assert_eq!(parts[1], "script", "Second part should be 'script'");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_trigger_notify_runnable_version_change_flow(db: Pool<Postgres>) {
|
||||
// First create a flow with empty versions array
|
||||
let flow_path = format!("f/test/flow_{}", uuid::Uuid::new_v4());
|
||||
@@ -494,7 +492,7 @@ async fn test_trigger_notify_runnable_version_change_flow(db: Pool<Postgres>) {
|
||||
// Concurrent Access Tests
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_concurrent_event_insertion(db: Pool<Postgres>) {
|
||||
// Use a unique channel name for this test run
|
||||
let channel = format!("test_concurrent_{}", uuid::Uuid::new_v4());
|
||||
@@ -536,7 +534,7 @@ async fn test_concurrent_event_insertion(db: Pool<Postgres>) {
|
||||
assert_eq!(ids.len(), 10, "All events should have unique IDs");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_polling_isolation(db: Pool<Postgres>) {
|
||||
// Use a unique channel name for this test
|
||||
let channel = format!("test_isolation_{}", uuid::Uuid::new_v4());
|
||||
@@ -591,7 +589,7 @@ async fn test_polling_isolation(db: Pool<Postgres>) {
|
||||
// Edge Case Tests
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_empty_payload(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -607,7 +605,7 @@ async fn test_empty_payload(db: Pool<Postgres>) {
|
||||
assert_eq!(empty_events[0].payload, "", "Payload should be empty string");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_large_payload(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -625,7 +623,7 @@ async fn test_large_payload(db: Pool<Postgres>) {
|
||||
assert_eq!(large_events[0].payload.len(), 1024, "Payload should be preserved");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_special_characters_in_payload(db: Pool<Postgres>) {
|
||||
let before_id = get_latest_event_id(&db).await.unwrap();
|
||||
|
||||
@@ -642,7 +640,7 @@ async fn test_special_characters_in_payload(db: Pool<Postgres>) {
|
||||
assert_eq!(special_events[0].payload, special_payload, "Special characters should be preserved");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base"))]
|
||||
async fn test_cleanup_with_no_old_events(db: Pool<Postgres>) {
|
||||
// Use a unique channel name for this test
|
||||
let channel = format!("test_no_old_{}", uuid::Uuid::new_v4());
|
||||
@@ -114,7 +114,7 @@ mod tests {
|
||||
// ==================== Static Token Tests ====================
|
||||
|
||||
/// Test Vault connection with static token
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_vault_connection_static_token(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
|
||||
@@ -132,7 +132,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Test basic CRUD operations with static token
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_vault_crud_static_token(_db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
|
||||
@@ -193,7 +193,7 @@ mod tests {
|
||||
|
||||
/// Test Vault connection with JWT authentication
|
||||
#[cfg(feature = "openidconnect")]
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_vault_connection_jwt(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
setup_base_url().await;
|
||||
@@ -215,7 +215,7 @@ mod tests {
|
||||
|
||||
/// Test basic CRUD operations with JWT authentication
|
||||
#[cfg(feature = "openidconnect")]
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_vault_crud_jwt(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
setup_base_url().await;
|
||||
@@ -260,7 +260,7 @@ mod tests {
|
||||
// ==================== Migration Tests ====================
|
||||
|
||||
/// Test migration from database to Vault
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_migrate_db_to_vault(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
|
||||
@@ -314,7 +314,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Test migration from Vault back to database
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_migrate_vault_to_db(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
|
||||
@@ -373,7 +373,7 @@ mod tests {
|
||||
// ==================== Variable Rename Tests ====================
|
||||
|
||||
/// Test renaming a variable path in Vault
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_variable_rename(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
let _ = &db; // suppress unused warning
|
||||
@@ -445,7 +445,7 @@ mod tests {
|
||||
// ==================== Full Round Trip Test ====================
|
||||
|
||||
/// Test full round-trip: DB -> Vault -> DB with verification
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_full_round_trip(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
|
||||
@@ -501,7 +501,7 @@ mod tests {
|
||||
// ==================== Workspace Isolation Test ====================
|
||||
|
||||
/// Test that workspace isolation is maintained
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
async fn test_workspace_isolation(db: Pool<Postgres>) {
|
||||
skip_if_no_vault!();
|
||||
|
||||
@@ -27,8 +27,6 @@ use windmill_common::secret_backend::{
|
||||
SecretBackend, VaultSettings,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
fn test_vault_settings() -> VaultSettings {
|
||||
VaultSettings {
|
||||
address: std::env::var("VAULT_ADDR").unwrap_or_else(|_| "http://127.0.0.1:8200".to_string()),
|
||||
@@ -42,7 +40,7 @@ fn test_vault_settings() -> VaultSettings {
|
||||
}
|
||||
|
||||
/// Test that we can connect to Vault
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
#[ignore = "requires running Vault instance"]
|
||||
async fn test_vault_connection_works(db: Pool<Postgres>) {
|
||||
let settings = test_vault_settings();
|
||||
@@ -53,7 +51,7 @@ async fn test_vault_connection_works(db: Pool<Postgres>) {
|
||||
}
|
||||
|
||||
/// Test migration from database to Vault
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
#[ignore = "requires running Vault instance"]
|
||||
async fn test_migrate_db_to_vault(db: Pool<Postgres>) {
|
||||
let settings = test_vault_settings();
|
||||
@@ -119,7 +117,7 @@ async fn test_migrate_db_to_vault(db: Pool<Postgres>) {
|
||||
}
|
||||
|
||||
/// Test migration from Vault to database
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
#[ignore = "requires running Vault instance"]
|
||||
async fn test_migrate_vault_to_db(db: Pool<Postgres>) {
|
||||
let settings = test_vault_settings();
|
||||
@@ -196,7 +194,7 @@ async fn test_migrate_vault_to_db(db: Pool<Postgres>) {
|
||||
}
|
||||
|
||||
/// Test full round-trip migration: DB -> Vault -> DB
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
#[ignore = "requires running Vault instance"]
|
||||
async fn test_full_round_trip_migration(db: Pool<Postgres>) {
|
||||
let settings = test_vault_settings();
|
||||
@@ -276,7 +274,7 @@ async fn test_full_round_trip_migration(db: Pool<Postgres>) {
|
||||
}
|
||||
|
||||
/// Test that workspace isolation is maintained during migration
|
||||
#[sqlx::test(fixtures("base", "secret_backend"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "secret_backend"))]
|
||||
#[ignore = "requires running Vault instance"]
|
||||
async fn test_workspace_isolation(db: Pool<Postgres>) {
|
||||
let settings = test_vault_settings();
|
||||
146
backend/windmill-queue/tests/fixtures/base.sql
vendored
Normal file
146
backend/windmill-queue/tests/fixtures/base.sql
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
-- used for backend automated testing
|
||||
-- https://docs.rs/sqlx/latest/sqlx/attr.test.html
|
||||
|
||||
INSERT INTO workspace
|
||||
(id, name, owner)
|
||||
VALUES ('test-workspace', 'test-workspace', 'test-user');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test@windmill.dev', 'test-user', true, 'Admin');
|
||||
|
||||
INSERT INTO workspace_key(workspace_id, kind, key) VALUES
|
||||
('test-workspace', 'cloud', 'test-key');
|
||||
|
||||
|
||||
INSERT INTO workspace_settings (workspace_id) VALUES
|
||||
('test-workspace');
|
||||
|
||||
INSERT INTO group_ (workspace_id, name, summary, extra_perms) VALUES
|
||||
('test-workspace', 'all', 'All users', '{}');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name, username)
|
||||
VALUES ('test@windmill.dev', 'not-a-real-hash', 'password', true, true, 'Test User', 'test-user');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES ('test2@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 2');
|
||||
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES ('test3@windmill.dev', 'not-a-real-hash', 'password', false, true, 'Test User 3');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test2@windmill.dev', 'test-user-2', false, 'User');
|
||||
|
||||
INSERT INTO usr(workspace_id, email, username, is_admin, role) VALUES
|
||||
('test-workspace', 'test3@windmill.dev', 'test-user-3', false, 'User');
|
||||
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN', 'test@windmill.dev', 'test token', true);
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN_2', 'test2@windmill.dev', 'test token 2', false);
|
||||
insert INTO token(token, email, label, super_admin) VALUES ('SECRET_TOKEN_3', 'test3@windmill.dev', 'test token 3', false);
|
||||
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_admin;
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_user;
|
||||
|
||||
CREATE FUNCTION "notify_insert_on_completed_job" ()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('completed', NEW.id::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER "notify_insert_on_completed_job"
|
||||
AFTER INSERT ON "v2_job_completed"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION "notify_insert_on_completed_job" ();
|
||||
|
||||
|
||||
CREATE FUNCTION "notify_queue" ()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('queued', NEW.id::text);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER "notify_queue_after_insert"
|
||||
AFTER INSERT ON "v2_job_queue"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION "notify_queue" ();
|
||||
|
||||
CREATE TRIGGER "notify_queue_after_flow_status_update"
|
||||
AFTER UPDATE ON "v2_job_status"
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.flow_status IS DISTINCT FROM OLD.flow_status)
|
||||
EXECUTE FUNCTION "notify_queue" ();
|
||||
|
||||
-- Apply phase 4:
|
||||
DROP FUNCTION IF EXISTS v2_job_after_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_completed_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_completed_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_after_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_queue_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_runtime_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_runtime_before_update CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_status_before_insert CASCADE;
|
||||
DROP FUNCTION IF EXISTS v2_job_status_before_update CASCADE;
|
||||
|
||||
DROP VIEW IF EXISTS completed_job, completed_job_view, job, queue, queue_view CASCADE;
|
||||
|
||||
ALTER TABLE v2_job_queue
|
||||
DROP COLUMN IF EXISTS __parent_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_by CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_hash CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __args CASCADE,
|
||||
DROP COLUMN IF EXISTS __logs CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_code CASCADE,
|
||||
DROP COLUMN IF EXISTS __canceled CASCADE,
|
||||
DROP COLUMN IF EXISTS __last_ping CASCADE,
|
||||
DROP COLUMN IF EXISTS __job_kind CASCADE,
|
||||
DROP COLUMN IF EXISTS __env_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __schedule_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __permissioned_as CASCADE,
|
||||
DROP COLUMN IF EXISTS __flow_status CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_flow CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_flow_step CASCADE,
|
||||
DROP COLUMN IF EXISTS __language CASCADE,
|
||||
DROP COLUMN IF EXISTS __same_worker CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_lock CASCADE,
|
||||
DROP COLUMN IF EXISTS __pre_run_error CASCADE,
|
||||
DROP COLUMN IF EXISTS __email CASCADE,
|
||||
DROP COLUMN IF EXISTS __visible_to_owner CASCADE,
|
||||
DROP COLUMN IF EXISTS __mem_peak CASCADE,
|
||||
DROP COLUMN IF EXISTS __root_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __leaf_jobs CASCADE,
|
||||
DROP COLUMN IF EXISTS __concurrent_limit CASCADE,
|
||||
DROP COLUMN IF EXISTS __concurrency_time_window_s CASCADE,
|
||||
DROP COLUMN IF EXISTS __timeout CASCADE,
|
||||
DROP COLUMN IF EXISTS __flow_step_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __cache_ttl CASCADE;
|
||||
|
||||
LOCK TABLE v2_job_queue IN ACCESS EXCLUSIVE MODE;
|
||||
ALTER TABLE v2_job_completed
|
||||
DROP COLUMN IF EXISTS __parent_job CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_by CASCADE,
|
||||
DROP COLUMN IF EXISTS __created_at CASCADE,
|
||||
DROP COLUMN IF EXISTS __success CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_hash CASCADE,
|
||||
DROP COLUMN IF EXISTS __script_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __args CASCADE,
|
||||
DROP COLUMN IF EXISTS __logs CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_code CASCADE,
|
||||
DROP COLUMN IF EXISTS __canceled CASCADE,
|
||||
DROP COLUMN IF EXISTS __job_kind CASCADE,
|
||||
DROP COLUMN IF EXISTS __env_id CASCADE,
|
||||
DROP COLUMN IF EXISTS __schedule_path CASCADE,
|
||||
DROP COLUMN IF EXISTS __permissioned_as CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_flow CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_flow_step CASCADE,
|
||||
DROP COLUMN IF EXISTS __language CASCADE,
|
||||
DROP COLUMN IF EXISTS __is_skipped CASCADE,
|
||||
DROP COLUMN IF EXISTS __raw_lock CASCADE,
|
||||
DROP COLUMN IF EXISTS __email CASCADE,
|
||||
DROP COLUMN IF EXISTS __visible_to_owner CASCADE,
|
||||
DROP COLUMN IF EXISTS __tag CASCADE,
|
||||
DROP COLUMN IF EXISTS __priority CASCADE;
|
||||
84
backend/windmill-queue/tests/fixtures/schedule_push.sql
vendored
Normal file
84
backend/windmill-queue/tests/fixtures/schedule_push.sql
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
-- Fixture for schedule push tests
|
||||
-- Sets up scripts, flows, users, and schedules needed to test push_scheduled_job
|
||||
|
||||
-- Password entries for auth resolution
|
||||
INSERT INTO password (email, password_hash, login_type, super_admin, verified, name)
|
||||
VALUES
|
||||
('test@windmill.dev', 'dummy_hash', 'password', false, true, 'Test User'),
|
||||
('obo@windmill.dev', 'dummy_hash', 'password', false, true, 'OBO User')
|
||||
ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
-- OBO user in workspace
|
||||
INSERT INTO usr (workspace_id, email, username, is_admin, role)
|
||||
VALUES ('test-workspace', 'obo@windmill.dev', 'obo-user', false, 'Developer')
|
||||
ON CONFLICT (workspace_id, username) DO NOTHING;
|
||||
|
||||
-- A simple script
|
||||
INSERT INTO script (workspace_id, created_by, content, schema, summary, description, path, hash, language, lock, kind)
|
||||
VALUES (
|
||||
'test-workspace', 'test-user',
|
||||
'export async function main() { return "ok"; }',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'Test script', '', 'f/system/test_script', 100001, 'deno', '', 'script'
|
||||
);
|
||||
|
||||
-- A script with on_behalf_of_email
|
||||
INSERT INTO script (workspace_id, created_by, content, schema, summary, description, path, hash, language, lock, kind, on_behalf_of_email)
|
||||
VALUES (
|
||||
'test-workspace', 'test-user',
|
||||
'export async function main() { return "obo"; }',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'OBO script', '', 'f/system/obo_script', 100002, 'deno', '', 'script', 'obo@windmill.dev'
|
||||
);
|
||||
|
||||
-- A script with a tag
|
||||
INSERT INTO script (workspace_id, created_by, content, schema, summary, description, path, hash, language, lock, kind, tag)
|
||||
VALUES (
|
||||
'test-workspace', 'test-user',
|
||||
'export async function main() { return "tagged"; }',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'Tagged script', '', 'f/system/tagged_script', 100003, 'deno', '', 'script', 'custom-tag'
|
||||
);
|
||||
|
||||
-- A script with timeout
|
||||
INSERT INTO script (workspace_id, created_by, content, schema, summary, description, path, hash, language, lock, kind, timeout)
|
||||
VALUES (
|
||||
'test-workspace', 'test-user',
|
||||
'export async function main() { return "timeout"; }',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'Timeout script', '', 'f/system/timeout_script', 100004, 'deno', '', 'script', 300
|
||||
);
|
||||
|
||||
-- A flow
|
||||
INSERT INTO flow (workspace_id, summary, description, path, versions, schema, value, edited_by)
|
||||
VALUES (
|
||||
'test-workspace', 'Test flow', '', 'f/system/test_flow', '{200001}',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'{"modules": [{"id": "a", "value": {"path": "f/system/test_script", "type": "script", "input_transforms": {}}}]}',
|
||||
'test-user'
|
||||
);
|
||||
|
||||
INSERT INTO flow_version (id, workspace_id, path, schema, value, created_by)
|
||||
VALUES (
|
||||
200001, 'test-workspace', 'f/system/test_flow',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'{"modules": [{"id": "a", "value": {"path": "f/system/test_script", "type": "script", "input_transforms": {}}}]}',
|
||||
'test-user'
|
||||
);
|
||||
|
||||
-- A flow with on_behalf_of_email
|
||||
INSERT INTO flow (workspace_id, summary, description, path, versions, schema, value, edited_by, on_behalf_of_email)
|
||||
VALUES (
|
||||
'test-workspace', 'OBO flow', '', 'f/system/obo_flow', '{200002}',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'{"modules": [{"id": "a", "value": {"path": "f/system/test_script", "type": "script", "input_transforms": {}}}]}',
|
||||
'test-user', 'obo@windmill.dev'
|
||||
);
|
||||
|
||||
INSERT INTO flow_version (id, workspace_id, path, schema, value, created_by)
|
||||
VALUES (
|
||||
200002, 'test-workspace', 'f/system/obo_flow',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'{"modules": [{"id": "a", "value": {"path": "f/system/test_script", "type": "script", "input_transforms": {}}}]}',
|
||||
'test-user'
|
||||
);
|
||||
@@ -1,5 +1,3 @@
|
||||
mod common;
|
||||
|
||||
mod schedule_push {
|
||||
use chrono::Utc;
|
||||
use sqlx::{Pool, Postgres};
|
||||
@@ -118,7 +116,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: basic script schedule
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_script_schedule(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let authed = make_authed();
|
||||
@@ -140,7 +138,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: flow schedule
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_flow_schedule(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.is_flow = true;
|
||||
@@ -164,7 +162,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: on_behalf_of_email (script)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_script_on_behalf_of_email(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.script_path = "f/system/obo_script".to_string();
|
||||
@@ -191,7 +189,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: on_behalf_of_email (flow)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_flow_on_behalf_of_email(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.is_flow = true;
|
||||
@@ -218,7 +216,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: with retry wraps in SingleStepFlow
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_script_with_retry(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.retry = Some(serde_json::json!({
|
||||
@@ -247,7 +245,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: duplicate detection (same schedule + time = skip)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_duplicate_skipped(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let authed = make_authed();
|
||||
@@ -270,7 +268,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: invalid timezone
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_invalid_timezone(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.timezone = "Invalid/Timezone".to_string();
|
||||
@@ -288,7 +286,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: invalid cron expression
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_invalid_cron(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.schedule = "not a cron".to_string();
|
||||
@@ -306,7 +304,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: invalid args (not a dict)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_invalid_args(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
let raw = serde_json::value::RawValue::from_string("[1,2,3]".to_string()).unwrap();
|
||||
@@ -325,7 +323,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: with schedule args passed to job
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_with_args(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
let raw =
|
||||
@@ -353,7 +351,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: script not found
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_script_not_found(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.script_path = "f/system/nonexistent".to_string();
|
||||
@@ -371,7 +369,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: flow not found
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_flow_not_found(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.is_flow = true;
|
||||
@@ -390,7 +388,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: paused schedule (paused_until in future)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_paused_schedule(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.paused_until = Some(Utc::now() + chrono::Duration::hours(1));
|
||||
@@ -417,7 +415,7 @@ mod schedule_push {
|
||||
// push_scheduled_job: clock shift detection (now_cutoff >= now)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_clock_shift(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let authed = make_authed();
|
||||
@@ -445,7 +443,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: disabled schedule does not push
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_handle_disabled_schedule(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.enabled = false;
|
||||
@@ -471,7 +469,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: script path mismatch does not push
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_handle_path_mismatch(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let job = make_completed_job(&schedule);
|
||||
@@ -495,7 +493,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: enabled + matching path pushes next job
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_handle_enabled_pushes_next_job(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let job = make_completed_job(&schedule);
|
||||
@@ -524,7 +522,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: on_behalf_of_email via handle path
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_handle_on_behalf_of_email(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.script_path = "f/system/obo_script".to_string();
|
||||
@@ -559,7 +557,7 @@ mod schedule_push {
|
||||
// (caller is responsible for retry + eventual disable)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_handle_push_failure_disables_schedule(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -604,7 +602,7 @@ mod schedule_push {
|
||||
// If the caller commits, both the next tick and any prior writes persist.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_success_atomic_with_commit(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -646,7 +644,7 @@ mod schedule_push {
|
||||
// Ensures no next tick leaks when the outer tx is not committed.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_success_rolls_back_on_drop(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let job = make_completed_job(&schedule);
|
||||
@@ -675,7 +673,7 @@ mod schedule_push {
|
||||
// The schedule must stay enabled when the caller doesn't commit.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failure_disable_rolls_back_on_drop(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -721,7 +719,7 @@ mod schedule_push {
|
||||
// The caller can perform additional writes on the returned tx.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_tx_usable_after_success(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|_| {});
|
||||
let job = make_completed_job(&schedule);
|
||||
@@ -759,7 +757,7 @@ mod schedule_push {
|
||||
// The caller can still write on the returned tx after a schedule disable.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_tx_usable_after_failure(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -815,7 +813,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: flow schedule pushes next job
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_try_schedule_flow(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.is_flow = true;
|
||||
@@ -847,7 +845,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: script with retry wraps in SingleStepFlow
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_try_schedule_with_retry(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
let schedule = make_schedule(|s| {
|
||||
s.retry = Some(serde_json::json!({
|
||||
@@ -882,7 +880,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: push failure error message is stored on schedule
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_failure_stores_error_message(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -927,7 +925,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: disabled schedule leaves no side effects
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_disabled_schedule_no_side_effects(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -970,7 +968,7 @@ mod schedule_push {
|
||||
// try_schedule_next_job: path mismatch leaves schedule unchanged
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_path_mismatch_no_side_effects(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1012,7 +1010,7 @@ mod schedule_push {
|
||||
// doesn't error. The function should return (tx, None).
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_push_failure_schedule_not_in_db(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// Do NOT insert a schedule row — the disable UPDATE will match 0 rows
|
||||
let schedule = make_schedule(|s| {
|
||||
@@ -1044,7 +1042,7 @@ mod schedule_push {
|
||||
// unmodified enabled state are committed together.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_invariant_success_means_tick_committed(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1089,7 +1087,7 @@ mod schedule_push {
|
||||
// enabled and no next tick.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_invariant_failure_means_disabled_after_commit(
|
||||
db: Pool<Postgres>,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -1139,7 +1137,7 @@ mod schedule_push {
|
||||
// enabled so that zombie retry can re-attempt.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_invariant_rollback_preserves_schedule_for_retry(
|
||||
db: Pool<Postgres>,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -1187,6 +1185,7 @@ mod schedule_push {
|
||||
// Failpoint tests — feature-gated, only compiled under `failpoints`
|
||||
// ===================================================================
|
||||
|
||||
#[cfg(feature = "failpoints")]
|
||||
mod failpoint_tests {
|
||||
use super::*;
|
||||
use windmill_queue::jobs::schedule_failpoints::{ScheduleFailPoint, ACTIVE};
|
||||
@@ -1195,7 +1194,7 @@ mod schedule_push {
|
||||
// SavepointCreate failpoint → schedule disabled, 0 jobs
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_savepoint_create_disables(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1232,7 +1231,7 @@ mod schedule_push {
|
||||
// Push failpoint → schedule disabled, 0 jobs
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_push_disables(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1269,7 +1268,7 @@ mod schedule_push {
|
||||
// SavepointCommit failpoint → schedule disabled, 0 jobs
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_savepoint_commit_disables(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1306,7 +1305,7 @@ mod schedule_push {
|
||||
// ScheduleDisable failpoint → returns Some(err), caller doesn't commit
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_schedule_disable_returns_err(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1335,7 +1334,7 @@ mod schedule_push {
|
||||
// ScheduleDisable failpoint + tx drop → schedule stays enabled
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_disable_failure_rollback(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1375,7 +1374,7 @@ mod schedule_push {
|
||||
// no error handler notification (QuotaExceeded is silenced)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_push_quota_exceeded_script(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1414,7 +1413,7 @@ mod schedule_push {
|
||||
// PushQuotaExceeded failpoint (flow) → schedule disabled, 0 jobs
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_failpoint_push_quota_exceeded_flow(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, timezone, enabled, script_path, is_flow, email, extra_perms, ws_error_handler_muted, no_flow_overlap)
|
||||
@@ -1470,7 +1469,7 @@ mod schedule_push {
|
||||
// 4. The schedule remains enabled for retry on next flow execution
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[sqlx::test(fixtures("base", "schedule_push"))]
|
||||
#[sqlx::test(migrations = "../migrations", fixtures("base", "schedule_push"))]
|
||||
async fn test_zombie_flow_after_schedule_push_failure_meets_restart_criteria(
|
||||
db: Pool<Postgres>,
|
||||
) -> anyhow::Result<()> {
|
||||
37
backend/windmill-test-utils/Cargo.toml
Normal file
37
backend/windmill-test-utils/Cargo.toml
Normal file
@@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "windmill-test-utils"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_test_utils"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
private = []
|
||||
enterprise = []
|
||||
python = ["windmill-common/python"]
|
||||
deno_core = ["dep:windmill-runtime-nativets"]
|
||||
agent_worker_server = ["dep:windmill-api-agent-workers"]
|
||||
|
||||
[dependencies]
|
||||
windmill-api = { workspace = true, default-features = false }
|
||||
windmill-worker.workspace = true
|
||||
windmill-queue.workspace = true
|
||||
windmill-common = { workspace = true, default-features = false }
|
||||
windmill-api-client.workspace = true
|
||||
windmill-runtime-nativets = { workspace = true, optional = true }
|
||||
windmill-api-agent-workers = { workspace = true, optional = true }
|
||||
|
||||
sqlx.workspace = true
|
||||
futures.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
uuid.workspace = true
|
||||
chrono.workspace = true
|
||||
axum.workspace = true
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
@@ -574,7 +574,6 @@ pub async fn test_for_versions<F: Future<Output = ()>>(
|
||||
|
||||
use futures::StreamExt;
|
||||
|
||||
// #[cfg(feature = "python")]
|
||||
pub async fn assert_lockfile(
|
||||
db: &Pool<Postgres>,
|
||||
script_content: String,
|
||||
@@ -784,10 +784,14 @@ impl OpenAPISchema {
|
||||
}
|
||||
|
||||
/// Sanitizes this schema for Google AI's API by removing unsupported fields.
|
||||
/// Google's Gemini API does not accept JSON Schema metadata fields like $schema.
|
||||
/// See https://github.com/windmill-labs/windmill/issues/7759
|
||||
pub fn sanitize_for_google(&mut self) {
|
||||
// Remove $schema field - not supported by Google AI
|
||||
self.schema_url = None;
|
||||
self.default = None;
|
||||
self.exclusive_minimum = None;
|
||||
self.exclusive_maximum = None;
|
||||
self.r#const = None;
|
||||
self.multiple_of = None;
|
||||
|
||||
// Recursively sanitize nested schemas
|
||||
if let Some(ref mut items) = self.items {
|
||||
@@ -1493,6 +1497,8 @@ mod tests {
|
||||
schema_url: Some("http://json-schema.org/draft-07/schema#".to_string()),
|
||||
title: Some("Test Schema".to_string()),
|
||||
description: Some("A test schema".to_string()),
|
||||
minimum: Some(1.0),
|
||||
maximum: Some(100.0),
|
||||
properties: Some(
|
||||
vec![("name".to_string(), Box::new(string_schema()))]
|
||||
.into_iter()
|
||||
@@ -1512,5 +1518,131 @@ mod tests {
|
||||
assert!(schema.properties.is_some());
|
||||
assert!(schema.required.is_some());
|
||||
assert!(matches!(&schema.r#type, Some(SchemaType::Single(t)) if t == "object"));
|
||||
assert_eq!(schema.minimum, Some(1.0));
|
||||
assert_eq!(schema.maximum, Some(100.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_google_removes_default() {
|
||||
let mut schema = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("string".to_string())),
|
||||
default: Some(serde_json::Value::String("hello".to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
schema.sanitize_for_google();
|
||||
|
||||
assert!(schema.default.is_none(), "default should be removed");
|
||||
assert!(matches!(&schema.r#type, Some(SchemaType::Single(t)) if t == "string"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_google_removes_exclusive_minimum() {
|
||||
let mut schema = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("number".to_string())),
|
||||
exclusive_minimum: Some(0.0),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
schema.sanitize_for_google();
|
||||
|
||||
assert!(
|
||||
schema.exclusive_minimum.is_none(),
|
||||
"exclusiveMinimum should be removed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_google_removes_exclusive_maximum() {
|
||||
let mut schema = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("number".to_string())),
|
||||
exclusive_maximum: Some(100.0),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
schema.sanitize_for_google();
|
||||
|
||||
assert!(
|
||||
schema.exclusive_maximum.is_none(),
|
||||
"exclusiveMaximum should be removed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_google_removes_const() {
|
||||
let mut schema = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("string".to_string())),
|
||||
r#const: Some(serde_json::Value::String("fixed".to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
schema.sanitize_for_google();
|
||||
|
||||
assert!(schema.r#const.is_none(), "const should be removed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_google_removes_multiple_of() {
|
||||
let mut schema = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("integer".to_string())),
|
||||
multiple_of: Some(5.0),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
schema.sanitize_for_google();
|
||||
|
||||
assert!(
|
||||
schema.multiple_of.is_none(),
|
||||
"multipleOf should be removed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_google_removes_unsupported_fields_recursively() {
|
||||
let nested = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("number".to_string())),
|
||||
default: Some(serde_json::json!(42)),
|
||||
exclusive_minimum: Some(0.0),
|
||||
exclusive_maximum: Some(100.0),
|
||||
multiple_of: Some(2.0),
|
||||
r#const: Some(serde_json::json!(10)),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut schema = OpenAPISchema {
|
||||
r#type: Some(SchemaType::Single("object".to_string())),
|
||||
schema_url: Some("http://json-schema.org/draft-07/schema#".to_string()),
|
||||
default: Some(serde_json::json!({})),
|
||||
properties: Some(
|
||||
vec![("value".to_string(), Box::new(nested))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
schema.sanitize_for_google();
|
||||
|
||||
assert!(schema.schema_url.is_none());
|
||||
assert!(schema.default.is_none());
|
||||
|
||||
let value_prop = schema.properties.as_ref().unwrap().get("value").unwrap();
|
||||
assert!(value_prop.default.is_none(), "nested default should be removed");
|
||||
assert!(
|
||||
value_prop.exclusive_minimum.is_none(),
|
||||
"nested exclusiveMinimum should be removed"
|
||||
);
|
||||
assert!(
|
||||
value_prop.exclusive_maximum.is_none(),
|
||||
"nested exclusiveMaximum should be removed"
|
||||
);
|
||||
assert!(
|
||||
value_prop.multiple_of.is_none(),
|
||||
"nested multipleOf should be removed"
|
||||
);
|
||||
assert!(value_prop.r#const.is_none(), "nested const should be removed");
|
||||
|
||||
assert!(schema.properties.is_some());
|
||||
assert!(matches!(&schema.r#type, Some(SchemaType::Single(t)) if t == "object"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.631.0";
|
||||
export const VERSION = "v1.631.2";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -77,7 +77,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.631.0";
|
||||
export const VERSION = "1.631.2";
|
||||
|
||||
// Re-exported from constants.ts to maintain backwards compatibility
|
||||
export { WM_FORK_PREFIX } from "./core/constants.ts";
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
libxslt.dev
|
||||
libclang.dev
|
||||
curl.dev
|
||||
zlib.dev
|
||||
libffi # For deno_ffi
|
||||
libtool
|
||||
nodejs
|
||||
@@ -324,6 +325,9 @@
|
||||
|
||||
# LD_LIBRARY_PATH set in shellHook with a wrapper to avoid leaking into git/ssh
|
||||
# LD_LIBRARY_PATH = "${pkgs.gcc.lib}/lib";
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
|
||||
pkgs.zlib
|
||||
];
|
||||
|
||||
# Set C flags for Rust's bindgen program. Unlike ordinary C
|
||||
# compilation, bindgen does not invoke $CC directly. Instead it
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.631.0",
|
||||
"version": "1.631.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.631.0",
|
||||
"version": "1.631.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.631.0",
|
||||
"version": "1.631.2",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -372,7 +372,7 @@
|
||||
label: 'Reset to all tags minus native ones',
|
||||
onClick: () => {
|
||||
if (nconfig != undefined) {
|
||||
nconfig.worker_tags = defaultTags.concat(nativeTags)
|
||||
nconfig.worker_tags = defaultTags
|
||||
}
|
||||
},
|
||||
disabled: !canEditConfig,
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
>
|
||||
<div class="leading-6 font-semibold text-sm w-full flex justify-between">
|
||||
<div>Migrate to CSS editor</div><CloseButton
|
||||
onClick={() => (migrationModalOpen = false)}
|
||||
on:close={() => (migrationModalOpen = false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
<CloseButton
|
||||
noBg
|
||||
small
|
||||
onClick={() => {
|
||||
on:close={() => {
|
||||
items = items.filter((_, i) => i !== index)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
bind:value={items[index].value}
|
||||
/>
|
||||
<div class="absolute right-8">
|
||||
<CloseButton noBg small onClick={() => deleteSubgrid(index)} />
|
||||
<CloseButton noBg small on:close={() => deleteSubgrid(index)} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center gap-2">
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
/>
|
||||
{#if deletable}
|
||||
<div class="flex flex-row-reverse -mt-4">
|
||||
<CloseButton noBg onClick={() => dispatch('delete', k)} />
|
||||
<CloseButton noBg on:close={() => dispatch('delete', k)} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<CloseButton
|
||||
small
|
||||
onClick={() => deleteComponent(component.id, item.originalIndex)}
|
||||
on:close={() => deleteComponent(component.id, item.originalIndex)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<div
|
||||
class="flex justify-between {right ? '' : 'flex-row-reverse'} items-center gap-1 px-3 py-2"
|
||||
>
|
||||
<CloseButton onClick={() => secondaryMenu?.close()} />
|
||||
<CloseButton on:close={() => secondaryMenu?.close()} />
|
||||
{#if $secondaryMenu?.props?.type === 'style'}
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<div class="text-xs font-bold"> Style Panel</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Button from './button/Button.svelte'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
@@ -9,15 +10,17 @@
|
||||
Icon?: any | undefined
|
||||
class?: string
|
||||
id?: string | undefined
|
||||
onClick?: (e: Event) => void
|
||||
onClick?: () => void | undefined | any
|
||||
}
|
||||
|
||||
let { noBg = false, small = false, Icon, class: className, id, onClick }: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<Button
|
||||
{onClick}
|
||||
onPointerdown={(e) => e.stopPropagation()}
|
||||
on:click={() => (dispatch('close'), onClick?.())}
|
||||
on:pointerdown={(e) => e.stopPropagation()}
|
||||
{id}
|
||||
startIcon={{ icon: Icon ?? X }}
|
||||
iconOnly
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
dropdownBtnClasses?: string
|
||||
dropdownItems?: MenuItem[] | (() => MenuItem[]) | undefined
|
||||
hideDropdown?: boolean
|
||||
onClick?: (e: Event) => void
|
||||
onClick?: (e?: Event) => void
|
||||
children?: import('svelte').Snippet
|
||||
tooltip?: import('svelte').Snippet
|
||||
[key: string]: any
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CloseButton onClick={() => dispatch('close')} Icon={CloseIcon} id="{id}-close-btn" />
|
||||
<CloseButton on:close Icon={CloseIcon} id="{id}-close-btn" />
|
||||
</div>
|
||||
<span class="font-semibold text-emphasis truncate text-lg max-w-sm"
|
||||
>{title ?? ''}
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
{style}
|
||||
>
|
||||
{#if kind == 'X'}
|
||||
<div class="absolute top-4 right-4"><CloseButton onClick={() => (open = false)} /></div
|
||||
<div class="absolute top-4 right-4"><CloseButton on:close={() => (open = false)} /></div
|
||||
>
|
||||
{/if}
|
||||
<div class="flex">
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
onblur={handleBlur}
|
||||
/>
|
||||
<span class="text-secondary text-2xs inline-grid [&>*]:col-start-1 [&>*]:row-start-1"
|
||||
><span class="invisible">days</span><span>{day !== 1 ? 'days' : 'day'}</span></span
|
||||
><span class="invisible">days</span><span>{day && day > 1 ? 'days' : 'day'}</span></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-baseline">
|
||||
@@ -166,7 +166,7 @@
|
||||
onblur={handleBlur}
|
||||
/>
|
||||
<span class="text-secondary text-2xs inline-grid [&>*]:col-start-1 [&>*]:row-start-1"
|
||||
><span class="invisible">hrs</span><span>{hour !== 1 ? 'hrs' : 'hr'}</span></span
|
||||
><span class="invisible">hrs</span><span>{hour && hour > 1 ? 'hrs' : 'hr'}</span></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-baseline">
|
||||
@@ -190,7 +190,7 @@
|
||||
onblur={handleBlur}
|
||||
/>
|
||||
<span class="text-secondary text-2xs inline-grid [&>*]:col-start-1 [&>*]:row-start-1"
|
||||
><span class="invisible">mins</span><span>{min !== 1 ? 'mins' : 'min'}</span></span
|
||||
><span class="invisible">mins</span><span>{min && min > 1 ? 'mins' : 'min'}</span></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-baseline">
|
||||
@@ -214,7 +214,7 @@
|
||||
onblur={handleBlur}
|
||||
/>
|
||||
<span class="text-secondary text-2xs inline-grid [&>*]:col-start-1 [&>*]:row-start-1"
|
||||
><span class="invisible">secs</span><span>{sec !== 1 ? 'secs' : 'sec'}</span></span
|
||||
><span class="invisible">secs</span><span>{sec && sec > 1 ? 'secs' : 'sec'}</span></span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,7 +223,7 @@
|
||||
class="bg-transparent text-secondary hover:text-primary"
|
||||
noBg
|
||||
small
|
||||
onClick={() => {
|
||||
on:close={() => {
|
||||
seconds = defaultValue
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<CloseButton
|
||||
class="text-hint bg-transparent border-none"
|
||||
small
|
||||
onClick={(e) => { e.stopPropagation(); onRemove(item) }}
|
||||
on:close={(e) => (onRemove(item), e.stopPropagation())}
|
||||
/>
|
||||
{/if}
|
||||
</li>
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
noBg
|
||||
class="ml-2 remove-all bg-transparent text-hint"
|
||||
small
|
||||
onClick={(e) => { e.stopPropagation(); clearValue() }}
|
||||
on:close={(e) => (clearValue(), e.stopPropagation())}
|
||||
/>
|
||||
{/if}
|
||||
<SelectDropdown
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
class="bg-transparent text-secondary hover:text-primary"
|
||||
noBg
|
||||
small
|
||||
onClick={clearValue}
|
||||
on:close={clearValue}
|
||||
/>
|
||||
</div>
|
||||
{:else if RightIcon}
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
{/if}
|
||||
</Cell>
|
||||
<Cell class="w-12">
|
||||
<CloseButton small onClick={() => removeDataTable(dataTableIndex)} />
|
||||
<CloseButton small on:close={() => removeDataTable(dataTableIndex)} />
|
||||
</Cell>
|
||||
</Row>
|
||||
{/each}
|
||||
|
||||
@@ -378,7 +378,7 @@
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell class="w-12">
|
||||
<CloseButton small onClick={() => removeDucklake(ducklakeIndex)} />
|
||||
<CloseButton small on:close={() => removeDucklake(ducklakeIndex)} />
|
||||
</Cell>
|
||||
</Row>
|
||||
{/each}
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
{#if tableRow[0] !== null}
|
||||
<CloseButton
|
||||
small
|
||||
onClick={() => {
|
||||
on:close={() => {
|
||||
if (s3ResourceSettings.secondaryStorage) {
|
||||
s3ResourceSettings.secondaryStorage.splice(idx - 1, 1)
|
||||
s3ResourceSettings.secondaryStorage = [...s3ResourceSettings.secondaryStorage]
|
||||
|
||||
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.631.0"
|
||||
wmill_pg = ">=1.631.0"
|
||||
wmill = ">=1.631.2"
|
||||
wmill_pg = ">=1.631.2"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: '3.0.3'
|
||||
|
||||
info:
|
||||
version: 1.631.0
|
||||
version: 1.631.2
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.631.0'
|
||||
ModuleVersion = '1.631.2'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.631.0"
|
||||
version = "1.631.2"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.631.0",
|
||||
"version": "1.631.2",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./sqlUtils.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.631.0",
|
||||
"version": "1.631.2",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.631.0
|
||||
1.631.2
|
||||
|
||||
Reference in New Issue
Block a user